mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-15 16:26:08 +03:00
ssh-integration: replace levels with flags, optimize implementation
Rewrote shell functions to support the two new flags for shell-integration-features: - ssh-env: TERM compatibility + best effort environment variable propagation (anything beyond TERM will depend on what the remote host allows) - ssh-terminfo: automatic terminfo installation with control socket orchestration - Flags work independently or combined Implementation optimizations: - ~65% code reduction through unified execution path - Eliminated GHOSTTY_SSH_INTEGRATION environment variable system - Replaced complex function dispatch with direct flag detection - Consolidated 4 cache helper functions into single _ghst_cache() utility - Simplified control socket management (removed multi-step orchestration) - Subsequent connections to cached hosts are now directly executed and more reliable New additions: - If ssh-terminfo is enabled, ghostty will be wrapped to provide users with convenient commands to invoke either of the two utility functions: `ghostty ssh-cache-list` and `ghostty ssh-cache-clear`
This commit is contained in:
@ -95,252 +95,103 @@ if [[ "$GHOSTTY_SHELL_FEATURES" == *"sudo"* && -n "$TERMINFO" ]]; then
|
|||||||
}
|
}
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# SSH
|
# SSH Integration
|
||||||
if [[ -n "$GHOSTTY_SSH_INTEGRATION" ]]; then
|
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-(env|terminfo) ]]; then
|
||||||
# Cache configuration
|
# Only define cache functions and variable if ssh-terminfo is enabled
|
||||||
_ghostty_cache_dir="${XDG_STATE_HOME:-$HOME/.local/state}/ghostty"
|
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-terminfo ]]; then
|
||||||
_ghostty_cache_file="$_ghostty_cache_dir/terminfo_hosts"
|
_cache="${XDG_STATE_HOME:-$HOME/.local/state}/ghostty/terminfo_hosts"
|
||||||
|
|
||||||
# Create cache directory with proper permissions
|
# Cache operations and utilities
|
||||||
[[ ! -d "$_ghostty_cache_dir" ]] && mkdir -p "$_ghostty_cache_dir" && chmod 700 "$_ghostty_cache_dir"
|
_ghst_cache() {
|
||||||
|
case $2 in
|
||||||
# Extract SSH target from arguments
|
chk) [[ -f $_cache ]] && grep -qFx "$1" "$_cache" 2>/dev/null ;;
|
||||||
_ghostty_get_ssh_target() {
|
add)
|
||||||
local target=""
|
mkdir -p "${_cache%/*}"
|
||||||
local skip_next=false
|
|
||||||
local args=("$@")
|
|
||||||
|
|
||||||
for ((i=0; i<${#args[@]}; i++)); do
|
|
||||||
local arg="${args[i]}"
|
|
||||||
|
|
||||||
# Skip if we're processing a flag's argument
|
|
||||||
[[ "$skip_next" == "true" ]] && { skip_next=false; continue; }
|
|
||||||
|
|
||||||
# Handle flags that take arguments
|
|
||||||
if [[ "$arg" =~ ^-[bcDEeFIiJLlmOopQRSWw]$ ]]; then
|
|
||||||
skip_next=true
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Handle combined short flags with values (e.g., -p22)
|
|
||||||
[[ "$arg" =~ ^-[bcDEeFIiJLlmOopQRSWw].+ ]] && continue
|
|
||||||
|
|
||||||
# Skip other flags
|
|
||||||
[[ "$arg" =~ ^- ]] && continue
|
|
||||||
|
|
||||||
# This should be our target
|
|
||||||
target="$arg"
|
|
||||||
break
|
|
||||||
done
|
|
||||||
|
|
||||||
# Handle user@host format
|
|
||||||
echo "${target##*@}"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Check if host has terminfo cached
|
|
||||||
_ghostty_host_has_terminfo() {
|
|
||||||
local host="$1"
|
|
||||||
[[ -f "$_ghostty_cache_file" ]] && grep -qFx "$host" "$_ghostty_cache_file" 2>/dev/null
|
|
||||||
}
|
|
||||||
|
|
||||||
# Add host to cache atomically
|
|
||||||
_ghostty_cache_host() {
|
|
||||||
local host="$1"
|
|
||||||
local temp_file
|
|
||||||
temp_file="$_ghostty_cache_file.$$"
|
|
||||||
|
|
||||||
# Merge existing cache with new host
|
|
||||||
{
|
{
|
||||||
[[ -f "$_ghostty_cache_file" ]] && cat "$_ghostty_cache_file"
|
[[ -f $_cache ]] && cat "$_cache"
|
||||||
echo "$host"
|
builtin echo "$1"
|
||||||
} | sort -u > "$temp_file"
|
} | sort -u >"$_cache.tmp" && mv "$_cache.tmp" "$_cache" && chmod 600 "$_cache"
|
||||||
|
;;
|
||||||
# Atomic replace with proper permissions
|
esac
|
||||||
mv -f "$temp_file" "$_ghostty_cache_file" && chmod 600 "$_ghostty_cache_file"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Remove host from cache (for maintenance)
|
function ghostty_ssh_cache_clear() {
|
||||||
_ghostty_uncache_host() {
|
rm -f "$_cache" 2>/dev/null && builtin echo "Ghostty SSH terminfo cache cleared." || builtin echo "No Ghostty SSH terminfo cache found."
|
||||||
local host="$1"
|
|
||||||
[[ -f "$_ghostty_cache_file" ]] || return 0
|
|
||||||
|
|
||||||
local temp_file="$_ghostty_cache_file.$$"
|
|
||||||
grep -vFx "$host" "$_ghostty_cache_file" > "$temp_file" 2>/dev/null || true
|
|
||||||
mv -f "$temp_file" "$_ghostty_cache_file"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Main SSH wrapper
|
function ghostty_ssh_cache_list() {
|
||||||
|
[[ -s $_cache ]] && builtin echo "Hosts with Ghostty terminfo installed:" && cat "$_cache" || builtin echo "No cached hosts found."
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
|
||||||
|
# SSH wrapper
|
||||||
ssh() {
|
ssh() {
|
||||||
case "$GHOSTTY_SSH_INTEGRATION" in
|
local e=() o=() c=() t
|
||||||
term-only) _ghostty_ssh_term_only "$@" ;;
|
|
||||||
basic) _ghostty_ssh_basic "$@" ;;
|
# Get target
|
||||||
full) _ghostty_ssh_full "$@" ;;
|
t=$(builtin command ssh -G "$@" 2>/dev/null | awk '/^(user|hostname) /{print $2}' | paste -sd'@')
|
||||||
*) _ghostty_ssh_basic "$@" ;; # Default to basic
|
|
||||||
|
# Set up env vars first so terminfo installation inherits them
|
||||||
|
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-env ]]; then
|
||||||
|
builtin export COLORTERM=${COLORTERM:-truecolor} TERM_PROGRAM=${TERM_PROGRAM:-ghostty} ${GHOSTTY_VERSION:+TERM_PROGRAM_VERSION=$GHOSTTY_VERSION}
|
||||||
|
for v in COLORTERM=truecolor TERM_PROGRAM=ghostty ${GHOSTTY_VERSION:+TERM_PROGRAM_VERSION=$GHOSTTY_VERSION}; do
|
||||||
|
o+=(-o "SendEnv ${v%=*}" -o "SetEnv $v")
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Install terminfo if needed, reuse control connection for main session
|
||||||
|
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-terminfo ]]; then
|
||||||
|
if [[ -n $t ]] && _ghst_cache "$t" chk; then
|
||||||
|
e+=(TERM=xterm-ghostty)
|
||||||
|
elif builtin command -v infocmp >/dev/null 2>&1; then
|
||||||
|
builtin local ti
|
||||||
|
ti=$(infocmp -x xterm-ghostty 2>/dev/null) || builtin echo "Warning: xterm-ghostty terminfo not found locally." >&2
|
||||||
|
if [[ -n $ti ]]; then
|
||||||
|
builtin echo "Setting up Ghostty terminfo on remote host..." >&2
|
||||||
|
builtin local cp
|
||||||
|
cp="/tmp/ghostty-ssh-$USER-$RANDOM-$(date +%s)"
|
||||||
|
case $(builtin echo "$ti" | builtin command ssh "${o[@]}" -o ControlMaster=yes -o ControlPath="$cp" -o ControlPersist=60s "$@" '
|
||||||
|
infocmp xterm-ghostty >/dev/null 2>&1 && echo OK && exit
|
||||||
|
command -v tic >/dev/null 2>&1 || { echo NO_TIC; exit 1; }
|
||||||
|
mkdir -p ~/.terminfo 2>/dev/null && tic -x - 2>/dev/null && echo OK || echo FAIL
|
||||||
|
') in
|
||||||
|
OK)
|
||||||
|
builtin echo "Terminfo setup complete." >&2
|
||||||
|
[[ -n $t ]] && _ghst_cache "$t" add
|
||||||
|
e+=(TERM=xterm-ghostty)
|
||||||
|
c+=(-o "ControlPath=$cp")
|
||||||
|
;;
|
||||||
|
*) builtin echo "Warning: Failed to install terminfo." >&2 ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
builtin echo "Warning: infocmp not found locally. Terminfo installation unavailable." >&2
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Fallback TERM only if terminfo didn't set it
|
||||||
|
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-env ]]; then
|
||||||
|
[[ $TERM == xterm-ghostty && ! " ${e[*]} " =~ " TERM=" ]] && e+=(TERM=xterm-256color)
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Execute
|
||||||
|
if [[ ${#e[@]} -gt 0 ]]; then
|
||||||
|
env "${e[@]}" ssh "${o[@]}" "${c[@]}" "$@"
|
||||||
|
else
|
||||||
|
builtin command ssh "${o[@]}" "${c[@]}" "$@"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# If 'ssh-terminfo' flag is enabled, wrap ghostty to provide 'ghostty ssh-cache-list' and `ghostty ssh-cache-clear` utility commands
|
||||||
|
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-terminfo ]]; then
|
||||||
|
ghostty() {
|
||||||
|
case "$1" in
|
||||||
|
ssh-cache-list) ghostty_ssh_cache_list ;;
|
||||||
|
ssh-cache-clear) ghostty_ssh_cache_clear ;;
|
||||||
|
*) builtin command ghostty "$@" ;;
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
# Level: term-only - Just fix TERM compatibility
|
|
||||||
_ghostty_ssh_term_only() {
|
|
||||||
if [[ "$TERM" == "xterm-ghostty" ]]; then
|
|
||||||
TERM=xterm-256color builtin command ssh "$@"
|
|
||||||
else
|
|
||||||
builtin command ssh "$@"
|
|
||||||
fi
|
fi
|
||||||
}
|
|
||||||
|
|
||||||
# Level: basic - TERM fix + environment propagation
|
|
||||||
_ghostty_ssh_basic() {
|
|
||||||
local term_value
|
|
||||||
term_value=$([[ "$TERM" == "xterm-ghostty" ]] && echo "xterm-256color" || echo "$TERM")
|
|
||||||
|
|
||||||
builtin command ssh "$@" "
|
|
||||||
# Set environment for this session
|
|
||||||
export GHOSTTY_SHELL_FEATURES='$GHOSTTY_SHELL_FEATURES'
|
|
||||||
export TERM='$term_value'
|
|
||||||
|
|
||||||
# Start interactive shell
|
|
||||||
exec \$SHELL -l
|
|
||||||
"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Level: full - Complete integration with terminfo
|
|
||||||
_ghostty_ssh_full() {
|
|
||||||
local target
|
|
||||||
target="$(_ghostty_get_ssh_target "$@")"
|
|
||||||
|
|
||||||
# Quick path for cached hosts
|
|
||||||
if [[ -n "$target" ]] && _ghostty_host_has_terminfo "$target"; then
|
|
||||||
# Direct connection with full ghostty support
|
|
||||||
builtin command ssh -t "$@" "
|
|
||||||
export GHOSTTY_SHELL_FEATURES='$GHOSTTY_SHELL_FEATURES'
|
|
||||||
export TERM='xterm-ghostty'
|
|
||||||
exec \$SHELL -l
|
|
||||||
"
|
|
||||||
return $?
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check if we can export terminfo
|
|
||||||
if ! builtin command -v infocmp >/dev/null 2>&1; then
|
|
||||||
echo "Warning: infocmp not found locally. Using basic integration." >&2
|
|
||||||
_ghostty_ssh_basic "$@"
|
|
||||||
return $?
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Generate terminfo data
|
|
||||||
local terminfo_data
|
|
||||||
terminfo_data="$(infocmp -x xterm-ghostty 2>/dev/null)" || {
|
|
||||||
echo "Warning: xterm-ghostty terminfo not found locally. Using basic integration." >&2
|
|
||||||
_ghostty_ssh_basic "$@"
|
|
||||||
return $?
|
|
||||||
}
|
|
||||||
|
|
||||||
echo "Setting up Ghostty terminal support on remote host..." >&2
|
|
||||||
|
|
||||||
# Create control socket path
|
|
||||||
local control_path="/tmp/ghostty-ssh-${USER}-$"
|
|
||||||
trap "rm -f '$control_path'" EXIT
|
|
||||||
|
|
||||||
# Start control master and check/install terminfo
|
|
||||||
local setup_script='
|
|
||||||
if ! infocmp xterm-ghostty >/dev/null 2>&1; then
|
|
||||||
if command -v tic >/dev/null 2>&1; then
|
|
||||||
mkdir -p "$HOME/.terminfo" 2>/dev/null
|
|
||||||
echo "NEEDS_INSTALL"
|
|
||||||
else
|
|
||||||
echo "NO_TIC"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "ALREADY_INSTALLED"
|
|
||||||
fi
|
|
||||||
'
|
|
||||||
|
|
||||||
# First connection: Start control master and check status
|
|
||||||
local install_status
|
|
||||||
install_status=$(builtin command ssh -o ControlMaster=yes \
|
|
||||||
-o ControlPath="$control_path" \
|
|
||||||
-o ControlPersist=30s \
|
|
||||||
"$@" "$setup_script")
|
|
||||||
|
|
||||||
case "$install_status" in
|
|
||||||
"NEEDS_INSTALL")
|
|
||||||
echo "Installing xterm-ghostty terminfo..." >&2
|
|
||||||
# Send terminfo through existing control connection
|
|
||||||
if echo "$terminfo_data" | builtin command ssh -o ControlPath="$control_path" "$@" \
|
|
||||||
'tic -x - 2>/dev/null && echo "SUCCESS"' | grep -q "SUCCESS"; then
|
|
||||||
echo "Terminfo installed successfully." >&2
|
|
||||||
[[ -n "$target" ]] && _ghostty_cache_host "$target"
|
|
||||||
else
|
|
||||||
echo "Warning: Failed to install terminfo. Using basic integration." >&2
|
|
||||||
ssh -O exit -o ControlPath="$control_path" "$@" 2>/dev/null || true
|
|
||||||
_ghostty_ssh_basic "$@"
|
|
||||||
return $?
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
"ALREADY_INSTALLED")
|
|
||||||
[[ -n "$target" ]] && _ghostty_cache_host "$target"
|
|
||||||
;;
|
|
||||||
"NO_TIC")
|
|
||||||
echo "Warning: tic not found on remote host. Using basic integration." >&2
|
|
||||||
ssh -O exit -o ControlPath="$control_path" "$@" 2>/dev/null || true
|
|
||||||
_ghostty_ssh_basic "$@"
|
|
||||||
return $?
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
# Now use the existing control connection for interactive session
|
|
||||||
echo "Connecting with full Ghostty support..." >&2
|
|
||||||
|
|
||||||
# Pass environment through and start login shell to show MOTD
|
|
||||||
builtin command ssh -t -o ControlPath="$control_path" "$@" "
|
|
||||||
# Set up Ghostty environment
|
|
||||||
export GHOSTTY_SHELL_FEATURES='$GHOSTTY_SHELL_FEATURES'
|
|
||||||
export TERM='xterm-ghostty'
|
|
||||||
|
|
||||||
# Display MOTD if this is a fresh connection
|
|
||||||
if [[ '$install_status' == 'NEEDS_INSTALL' ]]; then
|
|
||||||
# Try to display MOTD manually
|
|
||||||
if [[ -f /etc/motd ]]; then
|
|
||||||
cat /etc/motd 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
# Run update-motd if available (Ubuntu/Debian)
|
|
||||||
if [[ -d /etc/update-motd.d ]]; then
|
|
||||||
run-parts /etc/update-motd.d 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Force a login shell
|
|
||||||
exec \$SHELL -l
|
|
||||||
"
|
|
||||||
|
|
||||||
local exit_code=$?
|
|
||||||
|
|
||||||
# Clean up control socket
|
|
||||||
ssh -O exit -o ControlPath="$control_path" "$@" 2>/dev/null || true
|
|
||||||
|
|
||||||
return $exit_code
|
|
||||||
}
|
|
||||||
|
|
||||||
# Utility function to clear cache for a specific host
|
|
||||||
ghostty_ssh_reset() {
|
|
||||||
local host="${1:-}"
|
|
||||||
if [[ -z "$host" ]]; then
|
|
||||||
echo "Usage: ghostty_ssh_reset <hostname>" >&2
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
_ghostty_uncache_host "$host"
|
|
||||||
echo "Cleared Ghostty terminfo cache for: $host"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Utility function to list cached hosts
|
|
||||||
ghostty_ssh_list_cached() {
|
|
||||||
if [[ -f "$_ghostty_cache_file" ]]; then
|
|
||||||
echo "Hosts with cached Ghostty terminfo:"
|
|
||||||
cat "$_ghostty_cache_file"
|
|
||||||
else
|
|
||||||
echo "No hosts cached yet."
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Import bash-preexec, safe to do multiple times
|
# Import bash-preexec, safe to do multiple times
|
||||||
|
@ -99,174 +99,165 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
# SSH Integration
|
# SSH Integration
|
||||||
# Cache file for tracking hosts with terminfo installed
|
use str
|
||||||
var ghostty-cache-file = (if (has-env GHOSTTY_RESOURCES_DIR) { put $E:GHOSTTY_RESOURCES_DIR"/terminfo_hosts" } else { put $E:HOME"/.config/ghostty/terminfo_hosts" })
|
use path
|
||||||
|
use re
|
||||||
|
|
||||||
# Extract target host from SSH arguments
|
if (re:match 'ssh-(env|terminfo)' $E:GHOSTTY_SHELL_FEATURES) {
|
||||||
fn ghostty-get-ssh-target {|@args|
|
# Only define cache functions and variable if ssh-terminfo is enabled
|
||||||
var target = ""
|
if (re:match 'ssh-terminfo' $E:GHOSTTY_SHELL_FEATURES) {
|
||||||
var skip-next = $false
|
var _cache = (path:join (or $E:XDG_STATE_HOME $E:HOME/.local/state) ghostty terminfo_hosts)
|
||||||
|
|
||||||
for arg $args {
|
# Cache operations and utilities
|
||||||
if (eq $skip-next $true) {
|
fn _ghst_cache {|target action|
|
||||||
set skip-next = $false
|
if (eq $action chk) {
|
||||||
continue
|
if (path:is-regular $_cache) {
|
||||||
|
try {
|
||||||
|
grep -qFx $target $_cache 2>/dev/null
|
||||||
|
} catch e {
|
||||||
|
fail
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
# Skip flags that take arguments
|
fail
|
||||||
if (re:match '^-[bcDEeFIiJLlmOopQRSWw]$' $arg) {
|
|
||||||
set skip-next = $true
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
} elif (eq $action add) {
|
||||||
# Skip other flags
|
mkdir -p (path:dir $_cache)
|
||||||
if (re:match '^-' $arg) {
|
var tmpfile = $_cache.tmp
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
# This should be the target
|
|
||||||
set target = $arg
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
put $target
|
|
||||||
}
|
|
||||||
|
|
||||||
# Check if host has terminfo installed
|
|
||||||
fn ghostty-host-has-terminfo {|target|
|
|
||||||
and (path:is-regular $ghostty-cache-file) ?(grep -qFx $target $ghostty-cache-file 2>/dev/null)
|
|
||||||
}
|
|
||||||
|
|
||||||
# Add host to terminfo cache
|
|
||||||
fn ghostty-cache-host {|target|
|
|
||||||
var cache-dir = (path:dir $ghostty-cache-file)
|
|
||||||
|
|
||||||
# Create cache directory if needed
|
|
||||||
if (not (path:is-dir $cache-dir)) {
|
|
||||||
mkdir -p $cache-dir
|
|
||||||
}
|
|
||||||
|
|
||||||
# Atomic write to cache file
|
|
||||||
var temp-file = $ghostty-cache-file".tmp"
|
|
||||||
|
|
||||||
{
|
{
|
||||||
if (path:is-regular $ghostty-cache-file) {
|
if (path:is-regular $_cache) {
|
||||||
cat $ghostty-cache-file
|
cat $_cache
|
||||||
}
|
}
|
||||||
echo $target
|
echo $target
|
||||||
} | sort -u > $temp-file
|
} | sort -u > $tmpfile
|
||||||
|
mv $tmpfile $_cache
|
||||||
mv $temp-file $ghostty-cache-file
|
chmod 600 $_cache
|
||||||
|
|
||||||
# Secure permissions
|
|
||||||
?chmod 600 $ghostty-cache-file 2>/dev/null
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ssh-with-ghostty-integration {|@args|
|
|
||||||
if (has-env GHOSTTY_SSH_INTEGRATION) {
|
|
||||||
if (eq "term-only" $E:GHOSTTY_SSH_INTEGRATION) {
|
|
||||||
ssh-term-only $@args
|
|
||||||
} elif (eq "basic" $E:GHOSTTY_SSH_INTEGRATION) {
|
|
||||||
ssh-basic $@args
|
|
||||||
} elif (eq "full" $E:GHOSTTY_SSH_INTEGRATION) {
|
|
||||||
ssh-full $@args
|
|
||||||
} else {
|
|
||||||
# Unknown level, fall back to basic
|
|
||||||
ssh-basic $@args
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
(external ssh) $@args
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ssh-term-only {|@args|
|
fn ghostty_ssh_cache_clear {
|
||||||
# Level: term-only - Just fix TERM compatibility
|
|
||||||
if (eq "xterm-ghostty" $E:TERM) {
|
|
||||||
(external env) TERM=xterm-256color ssh $@args
|
|
||||||
} else {
|
|
||||||
(external ssh) $@args
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ssh-basic {|@args|
|
|
||||||
# Level: basic - TERM fix + environment variable propagation
|
|
||||||
var env-vars = []
|
|
||||||
|
|
||||||
# Fix TERM compatibility
|
|
||||||
if (eq "xterm-ghostty" $E:TERM) {
|
|
||||||
set env-vars = (conj $env-vars TERM=xterm-256color)
|
|
||||||
}
|
|
||||||
|
|
||||||
# Propagate Ghostty shell integration environment variables
|
|
||||||
if (not-eq "" $E:GHOSTTY_SHELL_FEATURES) {
|
|
||||||
set env-vars = (conj $env-vars GHOSTTY_SHELL_FEATURES=$E:GHOSTTY_SHELL_FEATURES)
|
|
||||||
}
|
|
||||||
|
|
||||||
# Execute with environment variables if any were set
|
|
||||||
if (> (count $env-vars) 0) {
|
|
||||||
(external env) $@env-vars ssh $@args
|
|
||||||
} else {
|
|
||||||
(external ssh) $@args
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ssh-full {|@args|
|
|
||||||
var target = (ghostty-get-ssh-target $@args)
|
|
||||||
|
|
||||||
# Check if we already know this host has terminfo
|
|
||||||
if (and (not-eq "" $target) (ghostty-host-has-terminfo $target)) {
|
|
||||||
# Direct connection with xterm-ghostty
|
|
||||||
var env-vars = [TERM=xterm-ghostty]
|
|
||||||
|
|
||||||
# Propagate Ghostty shell integration environment variables
|
|
||||||
if (not-eq "" $E:GHOSTTY_SHELL_FEATURES) {
|
|
||||||
set env-vars = (conj $env-vars GHOSTTY_SHELL_FEATURES=$E:GHOSTTY_SHELL_FEATURES)
|
|
||||||
}
|
|
||||||
|
|
||||||
(external env) $@env-vars ssh $@args
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
# Full integration: Install terminfo if needed
|
|
||||||
if (has-external infocmp) {
|
|
||||||
try {
|
try {
|
||||||
# Install terminfo only if needed
|
rm -f $_cache 2>/dev/null
|
||||||
infocmp -x xterm-ghostty 2>/dev/null | (external ssh) $@args '
|
echo "Ghostty SSH terminfo cache cleared."
|
||||||
if ! infocmp xterm-ghostty >/dev/null 2>&1; then
|
|
||||||
echo "Installing Ghostty terminfo..." >&2
|
|
||||||
tic -x - 2>/dev/null
|
|
||||||
fi
|
|
||||||
'
|
|
||||||
echo "Connecting with full Ghostty support..." >&2
|
|
||||||
|
|
||||||
# Cache this host for future connections
|
|
||||||
if (not-eq "" $target) {
|
|
||||||
ghostty-cache-host $target
|
|
||||||
}
|
|
||||||
|
|
||||||
# Connect with xterm-ghostty since terminfo is available
|
|
||||||
var env-vars = [TERM=xterm-ghostty]
|
|
||||||
|
|
||||||
# Propagate Ghostty shell integration environment variables
|
|
||||||
if (not-eq "" $E:GHOSTTY_SHELL_FEATURES) {
|
|
||||||
set env-vars = (conj $env-vars GHOSTTY_SHELL_FEATURES=$E:GHOSTTY_SHELL_FEATURES)
|
|
||||||
}
|
|
||||||
|
|
||||||
# Normal SSH connection with Ghostty terminfo available
|
|
||||||
(external env) $@env-vars ssh $@args
|
|
||||||
return
|
|
||||||
} catch e {
|
} catch e {
|
||||||
echo "Terminfo installation failed. Using basic integration." >&2
|
echo "No Ghostty SSH terminfo cache found."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# Fallback to basic integration
|
fn ghostty_ssh_cache_list {
|
||||||
ssh-basic $@args
|
if (and (path:is-regular $_cache) (> (wc -c < $_cache | str:trim-space) 0)) {
|
||||||
|
echo "Hosts with Ghostty terminfo installed:"
|
||||||
|
cat $_cache
|
||||||
|
} else {
|
||||||
|
echo "No cached hosts found."
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# Register SSH integration if enabled
|
# SSH wrapper
|
||||||
if (and (has-env GHOSTTY_SSH_INTEGRATION) (has-external ssh)) {
|
fn ssh {|@args|
|
||||||
edit:add-var ssh~ $ssh-with-ghostty-integration~
|
var e = []
|
||||||
|
var o = []
|
||||||
|
var c = []
|
||||||
|
var t = ""
|
||||||
|
|
||||||
|
# Get target (only if ssh-terminfo enabled for caching)
|
||||||
|
if (re:match 'ssh-terminfo' $E:GHOSTTY_SHELL_FEATURES) {
|
||||||
|
try {
|
||||||
|
set t = (e:ssh -G $@args 2>/dev/null | awk '/^(user|hostname) /{print $2}' | paste -sd'@' | str:trim-space)
|
||||||
|
} catch e {
|
||||||
|
# Ignore errors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Set up env vars first so terminfo installation inherits them
|
||||||
|
if (re:match 'ssh-env' $E:GHOSTTY_SHELL_FEATURES) {
|
||||||
|
set-env COLORTERM (or $E:COLORTERM truecolor)
|
||||||
|
set-env TERM_PROGRAM (or $E:TERM_PROGRAM ghostty)
|
||||||
|
if (has-env GHOSTTY_VERSION) {
|
||||||
|
set-env TERM_PROGRAM_VERSION $E:GHOSTTY_VERSION
|
||||||
|
}
|
||||||
|
|
||||||
|
var vars = [COLORTERM=truecolor TERM_PROGRAM=ghostty]
|
||||||
|
if (has-env GHOSTTY_VERSION) {
|
||||||
|
set vars = [$@vars TERM_PROGRAM_VERSION=$E:GHOSTTY_VERSION]
|
||||||
|
}
|
||||||
|
for v $vars {
|
||||||
|
var varname = (str:split &max=2 '=' $v | take 1)
|
||||||
|
set o = [$@o -o "SendEnv "$varname -o "SetEnv "$v]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Install terminfo if needed, reuse control connection for main session
|
||||||
|
if (re:match 'ssh-terminfo' $E:GHOSTTY_SHELL_FEATURES) {
|
||||||
|
if (and (not-eq $t "") (try { _ghst_cache $t chk } catch e { put $false })) {
|
||||||
|
set e = [$@e TERM=xterm-ghostty]
|
||||||
|
} elif (has-external infocmp) {
|
||||||
|
var ti = ""
|
||||||
|
try {
|
||||||
|
set ti = (infocmp -x xterm-ghostty 2>/dev/null | slurp)
|
||||||
|
} catch e {
|
||||||
|
echo "Warning: xterm-ghostty terminfo not found locally." >&2
|
||||||
|
}
|
||||||
|
if (not-eq $ti "") {
|
||||||
|
echo "Setting up Ghostty terminfo on remote host..." >&2
|
||||||
|
var cp = "/tmp/ghostty-ssh-"$E:USER"-"(randint 10000)"-"(date +%s | str:trim-space)
|
||||||
|
var result = (echo $ti | e:ssh $@o -o ControlMaster=yes -o ControlPath=$cp -o ControlPersist=60s $@args '
|
||||||
|
infocmp xterm-ghostty >/dev/null 2>&1 && echo OK && exit
|
||||||
|
command -v tic >/dev/null 2>&1 || { echo NO_TIC; exit 1; }
|
||||||
|
mkdir -p ~/.terminfo 2>/dev/null && tic -x - 2>/dev/null && echo OK || echo FAIL
|
||||||
|
' | str:trim-space)
|
||||||
|
if (eq $result OK) {
|
||||||
|
echo "Terminfo setup complete." >&2
|
||||||
|
if (not-eq $t "") {
|
||||||
|
_ghst_cache $t add
|
||||||
|
}
|
||||||
|
set e = [$@e TERM=xterm-ghostty]
|
||||||
|
set c = [$@c -o ControlPath=$cp]
|
||||||
|
} else {
|
||||||
|
echo "Warning: Failed to install terminfo." >&2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
echo "Warning: infocmp not found locally. Terminfo installation unavailable." >&2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Fallback TERM only if terminfo didn't set it
|
||||||
|
if (re:match 'ssh-env' $E:GHOSTTY_SHELL_FEATURES) {
|
||||||
|
if (and (eq $E:TERM xterm-ghostty) (not (re:match 'TERM=' (str:join ' ' $e)))) {
|
||||||
|
set e = [$@e TERM=xterm-256color]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Execute
|
||||||
|
if (> (count $e) 0) {
|
||||||
|
e:env $@e e:ssh $@o $@c $@args
|
||||||
|
} else {
|
||||||
|
e:ssh $@o $@c $@args
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Wrap ghostty command only if ssh-terminfo is enabled
|
||||||
|
if (re:match 'ssh-terminfo' $E:GHOSTTY_SHELL_FEATURES) {
|
||||||
|
fn ghostty {|@args|
|
||||||
|
if (eq $args[0] ssh-cache-list) {
|
||||||
|
ghostty_ssh_cache_list
|
||||||
|
} elif (eq $args[0] ssh-cache-clear) {
|
||||||
|
ghostty_ssh_cache_clear
|
||||||
|
} else {
|
||||||
|
(external ghostty) $@args
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
edit:add-var ghostty~ $ghostty~
|
||||||
|
|
||||||
|
# Export cache functions for global use
|
||||||
|
set edit:add-var[ghostty_ssh_cache_clear] = $ghostty_ssh_cache_clear~
|
||||||
|
set edit:add-var[ghostty_ssh_cache_list] = $ghostty_ssh_cache_list~
|
||||||
|
}
|
||||||
|
|
||||||
|
# Export ssh function for global use
|
||||||
|
set edit:add-var[ssh] = $ssh~
|
||||||
}
|
}
|
||||||
|
|
||||||
defer {
|
defer {
|
||||||
|
@ -86,156 +86,116 @@ function __ghostty_setup --on-event fish_prompt -d "Setup ghostty integration"
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# SSH integration
|
# SSH Integration
|
||||||
if test -n "$GHOSTTY_SSH_INTEGRATION"
|
if string match -qr 'ssh-(env|terminfo)' "$GHOSTTY_SHELL_FEATURES"
|
||||||
# Cache file for tracking hosts with terminfo installed
|
# Only define cache functions and variable if ssh-terminfo is enabled
|
||||||
set --local _ghostty_cache_file (string join / (test -n "$GHOSTTY_RESOURCES_DIR"; and echo "$GHOSTTY_RESOURCES_DIR"; or echo "$HOME/.config/ghostty") "terminfo_hosts")
|
if string match -qr 'ssh-terminfo' "$GHOSTTY_SHELL_FEATURES"
|
||||||
|
set -g _cache (test -n "$XDG_STATE_HOME" && echo "$XDG_STATE_HOME" || echo "$HOME/.local/state")/ghostty/terminfo_hosts
|
||||||
|
|
||||||
# Extract target host from SSH arguments
|
# Cache operations and utilities
|
||||||
function _ghostty_get_ssh_target
|
function _ghst_cache
|
||||||
set --local target ""
|
switch $argv[2]
|
||||||
set --local skip_next "false"
|
case chk
|
||||||
|
test -f $_cache && grep -qFx "$argv[1]" "$_cache" 2>/dev/null
|
||||||
for arg in $argv
|
case add
|
||||||
if test "$skip_next" = "true"
|
mkdir -p (dirname "$_cache")
|
||||||
set skip_next "false"
|
|
||||||
continue
|
|
||||||
end
|
|
||||||
|
|
||||||
# Skip flags that take arguments
|
|
||||||
if string match -qr -- '^-[bcDEeFIiJLlmOopQRSWw]$' "$arg"
|
|
||||||
set skip_next "true"
|
|
||||||
continue
|
|
||||||
end
|
|
||||||
|
|
||||||
# Skip other flags
|
|
||||||
if string match -q -- '-*' "$arg"
|
|
||||||
continue
|
|
||||||
end
|
|
||||||
|
|
||||||
# This should be the target
|
|
||||||
set target "$arg"
|
|
||||||
break
|
|
||||||
end
|
|
||||||
|
|
||||||
echo "$target"
|
|
||||||
end
|
|
||||||
|
|
||||||
# Check if host has terminfo installed
|
|
||||||
function _ghostty_host_has_terminfo
|
|
||||||
set --local target "$argv[1]"
|
|
||||||
test -f "$_ghostty_cache_file"; and grep -qFx "$target" "$_ghostty_cache_file" 2>/dev/null
|
|
||||||
end
|
|
||||||
|
|
||||||
# Add host to terminfo cache
|
|
||||||
function _ghostty_cache_host
|
|
||||||
set --local target "$argv[1]"
|
|
||||||
set --local cache_dir (dirname "$_ghostty_cache_file")
|
|
||||||
|
|
||||||
# Create cache directory if needed
|
|
||||||
test -d "$cache_dir"; or mkdir -p "$cache_dir"
|
|
||||||
|
|
||||||
# Atomic write to cache file
|
|
||||||
begin
|
begin
|
||||||
if test -f "$_ghostty_cache_file"
|
test -f $_cache && cat "$_cache"
|
||||||
cat "$_ghostty_cache_file"
|
builtin echo "$argv[1]"
|
||||||
|
end | sort -u >"$_cache.tmp" && mv "$_cache.tmp" "$_cache" && chmod 600 "$_cache"
|
||||||
end
|
end
|
||||||
echo "$target"
|
|
||||||
end | sort -u > "$_ghostty_cache_file.tmp"; and mv "$_ghostty_cache_file.tmp" "$_ghostty_cache_file"
|
|
||||||
|
|
||||||
# Secure permissions
|
|
||||||
chmod 600 "$_ghostty_cache_file" 2>/dev/null
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Wrap `ssh` command to provide Ghostty SSH integration
|
function ghostty_ssh_cache_clear -d "Clear Ghostty SSH terminfo cache"
|
||||||
function ssh -d "Wrap ssh to provide Ghostty SSH integration"
|
rm -f "$_cache" 2>/dev/null && builtin echo "Ghostty SSH terminfo cache cleared." || builtin echo "No Ghostty SSH terminfo cache found."
|
||||||
switch "$GHOSTTY_SSH_INTEGRATION"
|
end
|
||||||
case "term-only"
|
|
||||||
_ghostty_ssh_term-only $argv
|
function ghostty_ssh_cache_list -d "List hosts with Ghostty terminfo installed"
|
||||||
case "basic"
|
test -s $_cache && builtin echo "Hosts with Ghostty terminfo installed:" && cat "$_cache" || builtin echo "No cached hosts found."
|
||||||
_ghostty_ssh_basic $argv
|
end
|
||||||
case "full"
|
end
|
||||||
_ghostty_ssh_full $argv
|
|
||||||
|
# SSH wrapper
|
||||||
|
function ssh
|
||||||
|
set -l e
|
||||||
|
set -l o
|
||||||
|
set -l c
|
||||||
|
set -l t
|
||||||
|
|
||||||
|
# Get target (only if ssh-terminfo enabled for caching)
|
||||||
|
if string match -qr 'ssh-terminfo' "$GHOSTTY_SHELL_FEATURES"
|
||||||
|
set t (builtin command ssh -G $argv 2>/dev/null | awk '/^(user|hostname) /{print $2}' | paste -sd'@')
|
||||||
|
end
|
||||||
|
|
||||||
|
# Set up env vars first so terminfo installation inherits them
|
||||||
|
if string match -qr 'ssh-env' "$GHOSTTY_SHELL_FEATURES"
|
||||||
|
set -gx COLORTERM (test -n "$COLORTERM" && echo "$COLORTERM" || echo "truecolor")
|
||||||
|
set -gx TERM_PROGRAM (test -n "$TERM_PROGRAM" && echo "$TERM_PROGRAM" || echo "ghostty")
|
||||||
|
test -n "$GHOSTTY_VERSION" && set -gx TERM_PROGRAM_VERSION "$GHOSTTY_VERSION"
|
||||||
|
|
||||||
|
for v in COLORTERM=truecolor TERM_PROGRAM=ghostty (test -n "$GHOSTTY_VERSION" && echo "TERM_PROGRAM_VERSION=$GHOSTTY_VERSION")
|
||||||
|
set -l varname (string split -m1 '=' "$v")[1]
|
||||||
|
set o $o -o "SendEnv $varname" -o "SetEnv $v"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Install terminfo if needed, reuse control connection for main session
|
||||||
|
if string match -qr 'ssh-terminfo' "$GHOSTTY_SHELL_FEATURES"
|
||||||
|
if test -n "$t" && _ghst_cache "$t" chk
|
||||||
|
set e $e TERM=xterm-ghostty
|
||||||
|
else if command -v infocmp >/dev/null 2>&1
|
||||||
|
set -l ti
|
||||||
|
set ti (infocmp -x xterm-ghostty 2>/dev/null) || builtin echo "Warning: xterm-ghostty terminfo not found locally." >&2
|
||||||
|
if test -n "$ti"
|
||||||
|
builtin echo "Setting up Ghostty terminfo on remote host..." >&2
|
||||||
|
set -l cp "/tmp/ghostty-ssh-$USER-"(random)"-"(date +%s)
|
||||||
|
set -l result (builtin echo "$ti" | builtin command ssh $o -o ControlMaster=yes -o ControlPath="$cp" -o ControlPersist=60s $argv '
|
||||||
|
infocmp xterm-ghostty >/dev/null 2>&1 && echo OK && exit
|
||||||
|
command -v tic >/dev/null 2>&1 || { echo NO_TIC; exit 1; }
|
||||||
|
mkdir -p ~/.terminfo 2>/dev/null && tic -x - 2>/dev/null && echo OK || echo FAIL
|
||||||
|
')
|
||||||
|
switch $result
|
||||||
|
case OK
|
||||||
|
builtin echo "Terminfo setup complete." >&2
|
||||||
|
test -n "$t" && _ghst_cache "$t" add
|
||||||
|
set e $e TERM=xterm-ghostty
|
||||||
|
set c $c -o "ControlPath=$cp"
|
||||||
|
case '*'
|
||||||
|
builtin echo "Warning: Failed to install terminfo." >&2
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
builtin echo "Warning: infocmp not found locally. Terminfo installation unavailable." >&2
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Fallback TERM only if terminfo didn't set it
|
||||||
|
if string match -qr 'ssh-env' "$GHOSTTY_SHELL_FEATURES"
|
||||||
|
if test "$TERM" = xterm-ghostty && not string match -q '*TERM=*' "$e"
|
||||||
|
set e $e TERM=xterm-256color
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Execute
|
||||||
|
if test (count $e) -gt 0
|
||||||
|
env $e ssh $o $c $argv
|
||||||
|
else
|
||||||
|
builtin command ssh $o $c $argv
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Wrap ghostty command only if ssh-terminfo is enabled
|
||||||
|
if string match -qr 'ssh-terminfo' "$GHOSTTY_SHELL_FEATURES"
|
||||||
|
function ghostty -d "Wrap ghostty to provide cache management commands"
|
||||||
|
switch "$argv[1]"
|
||||||
|
case ssh-cache-list
|
||||||
|
ghostty_ssh_cache_list
|
||||||
|
case ssh-cache-clear
|
||||||
|
ghostty_ssh_cache_clear
|
||||||
case "*"
|
case "*"
|
||||||
# Unknown level, fall back to basic
|
command ghostty $argv
|
||||||
_ghostty_ssh_basic $argv
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Level: term-only - Just fix TERM compatibility
|
|
||||||
function _ghostty_ssh_term-only -d "SSH with TERM compatibility fix"
|
|
||||||
if test "$TERM" = "xterm-ghostty"
|
|
||||||
TERM=xterm-256color builtin command ssh $argv
|
|
||||||
else
|
|
||||||
builtin command ssh $argv
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Level: basic - TERM fix + environment variable propagation
|
|
||||||
function _ghostty_ssh_basic -d "SSH with TERM fix and environment propagation"
|
|
||||||
# Build environment variables to propagate
|
|
||||||
set --local env_vars
|
|
||||||
|
|
||||||
# Fix TERM compatibility
|
|
||||||
if test "$TERM" = "xterm-ghostty"
|
|
||||||
set --append env_vars TERM=xterm-256color
|
|
||||||
end
|
|
||||||
|
|
||||||
# Propagate Ghostty shell integration environment variables
|
|
||||||
if test -n "$GHOSTTY_SHELL_FEATURES"
|
|
||||||
set --append env_vars GHOSTTY_SHELL_FEATURES="$GHOSTTY_SHELL_FEATURES"
|
|
||||||
end
|
|
||||||
|
|
||||||
# Execute with environment variables if any were set
|
|
||||||
if test (count $env_vars) -gt 0
|
|
||||||
env $env_vars ssh $argv
|
|
||||||
else
|
|
||||||
builtin command ssh $argv
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Level: full - All features
|
|
||||||
function _ghostty_ssh_full
|
|
||||||
set --local target (_ghostty_get_ssh_target $argv)
|
|
||||||
|
|
||||||
# Check if we already know this host has terminfo
|
|
||||||
if test -n "$target"; and _ghostty_host_has_terminfo "$target"
|
|
||||||
# Direct connection with xterm-ghostty
|
|
||||||
set --local env_vars TERM=xterm-ghostty
|
|
||||||
if test -n "$GHOSTTY_SHELL_FEATURES"
|
|
||||||
set --append env_vars GHOSTTY_SHELL_FEATURES="$GHOSTTY_SHELL_FEATURES"
|
|
||||||
end
|
|
||||||
env $env_vars ssh $argv
|
|
||||||
return 0
|
|
||||||
end
|
|
||||||
|
|
||||||
# Full integration: Install terminfo if needed
|
|
||||||
if type -q infocmp
|
|
||||||
# Install terminfo only if needed
|
|
||||||
if infocmp -x xterm-ghostty 2>/dev/null | ssh $argv '
|
|
||||||
if ! infocmp xterm-ghostty >/dev/null 2>&1
|
|
||||||
echo "Installing Ghostty terminfo..." >&2
|
|
||||||
tic -x - 2>/dev/null
|
|
||||||
end
|
|
||||||
'
|
|
||||||
echo "Connecting with full Ghostty support..." >&2
|
|
||||||
|
|
||||||
# Cache this host for future connections
|
|
||||||
test -n "$target"; and _ghostty_cache_host "$target"
|
|
||||||
|
|
||||||
# Connect with xterm-ghostty since terminfo is available
|
|
||||||
set --local env_vars TERM=xterm-ghostty
|
|
||||||
if test -n "$GHOSTTY_SHELL_FEATURES"
|
|
||||||
set --append env_vars GHOSTTY_SHELL_FEATURES="$GHOSTTY_SHELL_FEATURES"
|
|
||||||
end
|
|
||||||
env $env_vars ssh $argv
|
|
||||||
builtin return 0
|
|
||||||
end
|
|
||||||
echo "Terminfo installation failed. Using basic integration." >&2
|
|
||||||
end
|
|
||||||
|
|
||||||
# Fallback to basic integration
|
|
||||||
_ghostty_ssh_basic $argv
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -244,157 +244,107 @@ _ghostty_deferred_init() {
|
|||||||
}
|
}
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# SSH
|
# SSH Integration
|
||||||
if [[ -n "$GHOSTTY_SSH_INTEGRATION" ]]; then
|
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-(env|terminfo) ]]; then
|
||||||
# Cache file for tracking hosts with terminfo installed
|
# Only define cache functions and variable if ssh-terminfo is enabled
|
||||||
_ghostty_cache_file="${GHOSTTY_RESOURCES_DIR:-$HOME/.config/ghostty}/terminfo_hosts"
|
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-terminfo ]]; then
|
||||||
|
_cache="${XDG_STATE_HOME:-$HOME/.local/state}/ghostty/terminfo_hosts"
|
||||||
|
|
||||||
# Extract target host from SSH arguments
|
# Cache operations and utilities
|
||||||
_ghostty_get_ssh_target() {
|
_ghst_cache() {
|
||||||
local target=""
|
case $2 in
|
||||||
local skip_next=false
|
chk) [[ -f $_cache ]] && grep -qFx "$1" "$_cache" 2>/dev/null ;;
|
||||||
|
add)
|
||||||
for arg in "$@"; do
|
mkdir -p "${_cache:h}"
|
||||||
if [[ "$skip_next" == "true" ]]; then
|
|
||||||
skip_next=false
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Skip flags that take arguments
|
|
||||||
if [[ "$arg" =~ ^-[bcDEeFIiJLlmOopQRSWw]$ ]]; then
|
|
||||||
skip_next=true
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Skip other flags
|
|
||||||
if [[ "$arg" =~ ^- ]]; then
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
|
|
||||||
# This should be the target
|
|
||||||
target="$arg"
|
|
||||||
break
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "$target"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Check if host has terminfo installed
|
|
||||||
_ghostty_host_has_terminfo() {
|
|
||||||
local target="$1"
|
|
||||||
[[ -f "$_ghostty_cache_file" ]] && grep -qFx "$target" "$_ghostty_cache_file" 2>/dev/null
|
|
||||||
}
|
|
||||||
|
|
||||||
# Add host to terminfo cache
|
|
||||||
_ghostty_cache_host() {
|
|
||||||
local target="$1"
|
|
||||||
local cache_dir
|
|
||||||
cache_dir="$(dirname "$_ghostty_cache_file")"
|
|
||||||
|
|
||||||
# Create cache directory if needed
|
|
||||||
[[ ! -d "$cache_dir" ]] && mkdir -p "$cache_dir"
|
|
||||||
|
|
||||||
# Atomic write to cache file
|
|
||||||
{
|
{
|
||||||
if [[ -f "$_ghostty_cache_file" ]]; then
|
[[ -f $_cache ]] && cat "$_cache"
|
||||||
cat "$_ghostty_cache_file"
|
builtin echo "$1"
|
||||||
fi
|
} | sort -u >"$_cache.tmp" && mv "$_cache.tmp" "$_cache" && chmod 600 "$_cache"
|
||||||
echo "$target"
|
|
||||||
} | sort -u > "$_ghostty_cache_file.tmp" && mv "$_ghostty_cache_file.tmp" "$_ghostty_cache_file"
|
|
||||||
|
|
||||||
# Secure permissions
|
|
||||||
chmod 600 "$_ghostty_cache_file" 2>/dev/null
|
|
||||||
}
|
|
||||||
|
|
||||||
# Wrap `ssh` command to provide Ghostty SSH integration
|
|
||||||
ssh() {
|
|
||||||
case "$GHOSTTY_SSH_INTEGRATION" in
|
|
||||||
"term-only")
|
|
||||||
_ghostty_ssh_term-only "$@"
|
|
||||||
;;
|
|
||||||
"basic")
|
|
||||||
_ghostty_ssh_basic "$@"
|
|
||||||
;;
|
|
||||||
"full")
|
|
||||||
_ghostty_ssh_full "$@"
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
# Unknown level, fall back to basic
|
|
||||||
_ghostty_ssh_basic "$@"
|
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
# Level: term-only - Just fix TERM compatibility
|
ghostty_ssh_cache_clear() {
|
||||||
_ghostty_ssh_term-only() {
|
rm -f "$_cache" 2>/dev/null && builtin echo "Ghostty SSH terminfo cache cleared." || builtin echo "No Ghostty SSH terminfo cache found."
|
||||||
if [[ "$TERM" == "xterm-ghostty" ]]; then
|
}
|
||||||
TERM=xterm-256color builtin command ssh "$@"
|
|
||||||
|
ghostty_ssh_cache_list() {
|
||||||
|
[[ -s $_cache ]] && builtin echo "Hosts with Ghostty terminfo installed:" && cat "$_cache" || builtin echo "No cached hosts found."
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
|
||||||
|
# SSH wrapper
|
||||||
|
ssh() {
|
||||||
|
local -a e o c
|
||||||
|
local t
|
||||||
|
|
||||||
|
# Get target (only if ssh-terminfo enabled for caching)
|
||||||
|
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-terminfo ]]; then
|
||||||
|
t=$(builtin command ssh -G "$@" 2>/dev/null | awk '/^(user|hostname) /{print $2}' | paste -sd'@')
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set up env vars first so terminfo installation inherits them
|
||||||
|
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-env ]]; then
|
||||||
|
builtin export COLORTERM=${COLORTERM:-truecolor} TERM_PROGRAM=${TERM_PROGRAM:-ghostty} ${GHOSTTY_VERSION:+TERM_PROGRAM_VERSION=$GHOSTTY_VERSION}
|
||||||
|
for v in COLORTERM=truecolor TERM_PROGRAM=ghostty ${GHOSTTY_VERSION:+TERM_PROGRAM_VERSION=$GHOSTTY_VERSION}; do
|
||||||
|
o+=(-o "SendEnv ${v%=*}" -o "SetEnv $v")
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Install terminfo if needed, reuse control connection for main session
|
||||||
|
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-terminfo ]]; then
|
||||||
|
if [[ -n $t ]] && _ghst_cache "$t" chk; then
|
||||||
|
e+=(TERM=xterm-ghostty)
|
||||||
|
elif builtin command -v infocmp >/dev/null 2>&1; then
|
||||||
|
local ti
|
||||||
|
ti=$(infocmp -x xterm-ghostty 2>/dev/null) || builtin echo "Warning: xterm-ghostty terminfo not found locally." >&2
|
||||||
|
if [[ -n $ti ]]; then
|
||||||
|
builtin echo "Setting up Ghostty terminfo on remote host..." >&2
|
||||||
|
local cp
|
||||||
|
cp="/tmp/ghostty-ssh-$USER-$RANDOM-$(date +%s)"
|
||||||
|
case $(builtin echo "$ti" | builtin command ssh "${o[@]}" -o ControlMaster=yes -o ControlPath="$cp" -o ControlPersist=60s "$@" '
|
||||||
|
infocmp xterm-ghostty >/dev/null 2>&1 && echo OK && exit
|
||||||
|
command -v tic >/dev/null 2>&1 || { echo NO_TIC; exit 1; }
|
||||||
|
mkdir -p ~/.terminfo 2>/dev/null && tic -x - 2>/dev/null && echo OK || echo FAIL
|
||||||
|
') in
|
||||||
|
OK)
|
||||||
|
builtin echo "Terminfo setup complete." >&2
|
||||||
|
[[ -n $t ]] && _ghst_cache "$t" add
|
||||||
|
e+=(TERM=xterm-ghostty)
|
||||||
|
c+=(-o "ControlPath=$cp")
|
||||||
|
;;
|
||||||
|
*) builtin echo "Warning: Failed to install terminfo." >&2 ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
builtin command ssh "$@"
|
builtin echo "Warning: infocmp not found locally. Terminfo installation unavailable." >&2
|
||||||
fi
|
fi
|
||||||
}
|
|
||||||
|
|
||||||
# Level: basic - TERM fix + environment variable propagation
|
|
||||||
_ghostty_ssh_basic() {
|
|
||||||
local env_vars=()
|
|
||||||
|
|
||||||
# Fix TERM compatibility
|
|
||||||
if [[ "$TERM" == "xterm-ghostty" ]]; then
|
|
||||||
env_vars+=("TERM=xterm-256color")
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Propagate Ghostty shell integration environment variables
|
# Fallback TERM only if terminfo didn't set it
|
||||||
[[ -n "$GHOSTTY_SHELL_FEATURES" ]] && env_vars+=("GHOSTTY_SHELL_FEATURES=$GHOSTTY_SHELL_FEATURES")
|
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-env ]]; then
|
||||||
|
[[ $TERM == xterm-ghostty && ! " ${(j: :)e} " =~ " TERM=" ]] && e+=(TERM=xterm-256color)
|
||||||
|
fi
|
||||||
|
|
||||||
# Execute with environment variables if any were set
|
# Execute
|
||||||
if [[ ${#env_vars[@]} -gt 0 ]]; then
|
if (( ${#e} > 0 )); then
|
||||||
env "${env_vars[@]}" ssh "$@"
|
env "${e[@]}" ssh "${o[@]}" "${c[@]}" "$@"
|
||||||
else
|
else
|
||||||
builtin command ssh "$@"
|
builtin command ssh "${o[@]}" "${c[@]}" "$@"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Level: full - All features
|
# Wrap ghostty command only if ssh-terminfo is enabled
|
||||||
_ghostty_ssh_full() {
|
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-terminfo ]]; then
|
||||||
local target
|
ghostty() {
|
||||||
target="$(_ghostty_get_ssh_target "$@")"
|
case "$1" in
|
||||||
|
ssh-cache-list) ghostty_ssh_cache_list ;;
|
||||||
# Check if we already know this host has terminfo
|
ssh-cache-clear) ghostty_ssh_cache_clear ;;
|
||||||
if [[ -n "$target" ]] && _ghostty_host_has_terminfo "$target"; then
|
*) builtin command ghostty "$@" ;;
|
||||||
# Direct connection with xterm-ghostty
|
esac
|
||||||
local env_vars=("TERM=xterm-ghostty")
|
|
||||||
[[ -n "$GHOSTTY_SHELL_FEATURES" ]] && env_vars+=("GHOSTTY_SHELL_FEATURES=$GHOSTTY_SHELL_FEATURES")
|
|
||||||
env "${env_vars[@]}" ssh "$@"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Full integration: Install terminfo if needed
|
|
||||||
if builtin command -v infocmp >/dev/null 2>&1; then
|
|
||||||
# Install terminfo only if needed
|
|
||||||
if infocmp -x xterm-ghostty 2>/dev/null | builtin command ssh "$@" '
|
|
||||||
if ! infocmp xterm-ghostty >/dev/null 2>&1; then
|
|
||||||
echo "Installing Ghostty terminfo..." >&2
|
|
||||||
tic -x - 2>/dev/null
|
|
||||||
fi
|
|
||||||
'; then
|
|
||||||
echo "Connecting with full Ghostty support..." >&2
|
|
||||||
|
|
||||||
# Cache this host for future connections
|
|
||||||
[[ -n "$target" ]] && _ghostty_cache_host "$target"
|
|
||||||
|
|
||||||
# Connect with xterm-ghostty since terminfo is available
|
|
||||||
local env_vars=("TERM=xterm-ghostty")
|
|
||||||
[[ -n "$GHOSTTY_SHELL_FEATURES" ]] && env_vars+=("GHOSTTY_SHELL_FEATURES=$GHOSTTY_SHELL_FEATURES")
|
|
||||||
env "${env_vars[@]}" ssh "$@"
|
|
||||||
builtin return 0
|
|
||||||
fi
|
|
||||||
echo "Terminfo installation failed. Using basic integration." >&2
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Fallback to basic integration
|
|
||||||
_ghostty_ssh_basic "$@"
|
|
||||||
}
|
}
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# Some zsh users manually run `source ~/.zshrc` in order to apply rc file
|
# Some zsh users manually run `source ~/.zshrc` in order to apply rc file
|
||||||
# changes to the current shell. This is a terrible practice that breaks many
|
# changes to the current shell. This is a terrible practice that breaks many
|
||||||
|
Reference in New Issue
Block a user