summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Rayne <yo@arcayne.dev>2025-06-17 17:43:21 -0700
committerJason Rayne <yo@arcayne.dev>2025-06-25 15:46:18 -0700
commit4cebee5c8e34a26861fea40034cd78abf679d98f (patch)
tree562e502214550bdb51d112f07698091dbef03148
parentb6bb9abfbcbc02212353283c56cf68491eba9266 (diff)
fix: add client-side caching to eliminate redundant terminfo installations
- Cache known hosts with terminfo in $GHOSTTY_RESOURCES_DIR/terminfo_hosts - Skip installation step for cached hosts (single connection instead of two) - Use secure file permissions (600) and atomic writes - Extract SSH target safely from command arguments - Maintains full functionality while improving user experience on repeated connections
-rw-r--r--src/shell-integration/bash/ghostty.bash98
-rw-r--r--src/shell-integration/elvish/lib/ghostty-integration.elv120
-rw-r--r--src/shell-integration/fish/vendor_conf.d/ghostty-shell-integration.fish99
-rw-r--r--src/shell-integration/zsh/ghostty-integration102
4 files changed, 367 insertions, 52 deletions
diff --git a/src/shell-integration/bash/ghostty.bash b/src/shell-integration/bash/ghostty.bash
index 2ce1f9503..51ab6c3e5 100644
--- a/src/shell-integration/bash/ghostty.bash
+++ b/src/shell-integration/bash/ghostty.bash
@@ -99,6 +99,66 @@ fi
# SSH
if [[ -n "$GHOSTTY_SSH_INTEGRATION" ]]; then
+ # Cache file for tracking hosts with terminfo installed
+ _ghostty_cache_file="${GHOSTTY_RESOURCES_DIR:-$HOME/.config/ghostty}/terminfo_hosts"
+
+ # Extract target host from SSH arguments
+ _ghostty_get_ssh_target() {
+ local target=""
+ local skip_next=false
+
+ for arg in "$@"; do
+ 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
+ cat "$_ghostty_cache_file"
+ fi
+ 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.
#
# This approach supports wrapping an `ssh` alias, but the alias definition
@@ -153,21 +213,35 @@ if [[ -n "$GHOSTTY_SSH_INTEGRATION" ]]; then
# Level: full - All features
_ghostty_ssh_full() {
- # Full integration: Two-step terminfo installation
- if builtin command -v infocmp >/dev/null 2>&1; then
- echo "Installing Ghostty terminfo on remote host..." >&2
-
- # Step 1: Install terminfo
- if infocmp -x xterm-ghostty 2>/dev/null | builtin command ssh "$@" 'tic -x - 2>/dev/null'; then
- echo "Terminfo installed successfully. Connecting with full Ghostty support..." >&2
+ local target
+ target="$(_ghostty_get_ssh_target "$@")"
+
+ # Check if we already know this host has terminfo
+ if [[ -n "$target" ]] && _ghostty_host_has_terminfo "$target"; then
+ # Direct connection with xterm-ghostty
+ 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
- # Step 2: Connect with xterm-ghostty since we know terminfo is now available
+ # 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")
-
- # Propagate Ghostty shell integration environment variables
[[ -n "$GHOSTTY_SHELL_FEATURES" ]] && env_vars+=("GHOSTTY_SHELL_FEATURES=$GHOSTTY_SHELL_FEATURES")
-
- # Normal SSH connection with Ghostty terminfo available
env "${env_vars[@]}" ssh "$@"
builtin return 0
fi
diff --git a/src/shell-integration/elvish/lib/ghostty-integration.elv b/src/shell-integration/elvish/lib/ghostty-integration.elv
index 2f938df97..deb258ae7 100644
--- a/src/shell-integration/elvish/lib/ghostty-integration.elv
+++ b/src/shell-integration/elvish/lib/ghostty-integration.elv
@@ -98,6 +98,70 @@
(external sudo) $@args
}
+ # SSH Integration
+ # Cache file for tracking hosts with terminfo installed
+ 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" })
+
+ # Extract target host from SSH arguments
+ fn ghostty-get-ssh-target {|@args|
+ var target = ""
+ var skip-next = $false
+
+ for arg $args {
+ if (eq $skip-next $true) {
+ set skip-next = $false
+ continue
+ }
+
+ # Skip flags that take arguments
+ if (re:match '^-[bcDEeFIiJLlmOopQRSWw]$' $arg) {
+ set skip-next = $true
+ continue
+ }
+
+ # Skip other flags
+ if (re:match '^-' $arg) {
+ 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) {
+ cat $ghostty-cache-file
+ }
+ echo $target
+ } | sort -u > $temp-file
+
+ mv $temp-file $ghostty-cache-file
+
+ # 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) {
@@ -127,17 +191,17 @@
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
@@ -147,22 +211,47 @@
}
fn ssh-full {|@args|
- # Full integration: Two-step terminfo installation
+ 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) {
- echo "Installing Ghostty terminfo on remote host..." >&2
-
try {
- infocmp -x xterm-ghostty 2>/dev/null | (external ssh) $@args 'tic -x - 2>/dev/null'
- echo "Terminfo installed successfully. Connecting with full Ghostty support..." >&2
-
- # Step 2: Connect with xterm-ghostty since we know terminfo is now available
+ # Install terminfo only if needed
+ infocmp -x xterm-ghostty 2>/dev/null | (external ssh) $@args '
+ 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
@@ -170,15 +259,16 @@
echo "Terminfo installation failed. Using basic integration." >&2
}
}
-
+
# Fallback to basic integration
ssh-basic $@args
}
-# Register SSH integration if enabled
+ # Register SSH integration if enabled
if (and (has-env GHOSTTY_SSH_INTEGRATION) (has-external ssh)) {
edit:add-var ssh~ $ssh-with-ghostty-integration~
- }
+ }
+
defer {
mark-prompt-start
report-pwd
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 0fe7155a1..bcf97cb82 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
@@ -86,8 +86,68 @@ function __ghostty_setup --on-event fish_prompt -d "Setup ghostty integration"
end
end
- # SSH integration wrapper
+ # SSH integration
if test -n "$GHOSTTY_SSH_INTEGRATION"
+ # Cache file for tracking hosts with terminfo installed
+ set -l _ghostty_cache_file (string join / (test -n "$GHOSTTY_RESOURCES_DIR"; and echo "$GHOSTTY_RESOURCES_DIR"; or echo "$HOME/.config/ghostty") "terminfo_hosts")
+
+ # Extract target host from SSH arguments
+ function _ghostty_get_ssh_target
+ set -l target ""
+ set -l skip_next false
+
+ for arg in $argv
+ if test "$skip_next" = "true"
+ 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 -l 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 -l target $argv[1]
+ set -l 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
+ if test -f "$_ghostty_cache_file"
+ cat "$_ghostty_cache_file"
+ 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
+
+ # Wrap `ssh` command to provide Ghostty SSH integration.
function ssh -d "Wrap ssh to provide Ghostty SSH integration"
switch "$GHOSTTY_SSH_INTEGRATION"
case term-only
@@ -136,22 +196,38 @@ function __ghostty_setup --on-event fish_prompt -d "Setup ghostty integration"
# Level: full - All features
function _ghostty_ssh_full
- # Full integration: Two-step terminfo installation
+ set -l 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
- echo "Installing Ghostty terminfo on remote host..." >&2
+ # 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"
- # Step 1: Install terminfo
- if infocmp -x xterm-ghostty 2>/dev/null | ssh $argv 'tic -x - 2>/dev/null'
- echo "Terminfo installed successfully. Connecting with full Ghostty support..." >&2
-
- # Step 2: Connect with xterm-ghostty since we know terminfo is now available
+ # Connect with xterm-ghostty since terminfo is available
set --local env_vars TERM=xterm-ghostty
-
- # Propagate Ghostty shell integration environment variables
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
@@ -162,6 +238,7 @@ function __ghostty_setup --on-event fish_prompt -d "Setup ghostty integration"
_ghostty_ssh_basic $argv
end
end
+
# Setup prompt marking
function __ghostty_mark_prompt_start --on-event fish_prompt --on-event fish_cancel --on-event fish_posterror
# If we never got the output end event, then we need to send it now.
diff --git a/src/shell-integration/zsh/ghostty-integration b/src/shell-integration/zsh/ghostty-integration
index 109c9fd60..c172fc02d 100644
--- a/src/shell-integration/zsh/ghostty-integration
+++ b/src/shell-integration/zsh/ghostty-integration
@@ -243,9 +243,69 @@ _ghostty_deferred_init() {
fi
}
fi
-
+
# SSH
if [[ -n "$GHOSTTY_SSH_INTEGRATION" ]]; then
+ # Cache file for tracking hosts with terminfo installed
+ _ghostty_cache_file="${GHOSTTY_RESOURCES_DIR:-$HOME/.config/ghostty}/terminfo_hosts"
+
+ # Extract target host from SSH arguments
+ _ghostty_get_ssh_target() {
+ local target=""
+ local skip_next=false
+
+ for arg in "$@"; do
+ 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
+ cat "$_ghostty_cache_file"
+ fi
+ 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
@@ -296,27 +356,41 @@ _ghostty_deferred_init() {
# Level: full - All features
_ghostty_ssh_full() {
- # Full integration: Two-step terminfo installation
+ local target
+ target="$(_ghostty_get_ssh_target "$@")"
+
+ # Check if we already know this host has terminfo
+ if [[ -n "$target" ]] && _ghostty_host_has_terminfo "$target"; then
+ # Direct connection with xterm-ghostty
+ 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
- echo "Installing Ghostty terminfo on remote host..." >&2
-
- # Step 1: Install terminfo
- if infocmp -x xterm-ghostty 2>/dev/null | builtin command ssh "$@" 'tic -x - 2>/dev/null'; then
- echo "Terminfo installed successfully. Connecting with full Ghostty support..." >&2
-
- # Step 2: Connect with xterm-ghostty since we know terminfo is now available
+ # 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")
-
- # Propagate Ghostty shell integration environment variables
[[ -n "$GHOSTTY_SHELL_FEATURES" ]] && env_vars+=("GHOSTTY_SHELL_FEATURES=$GHOSTTY_SHELL_FEATURES")
-
- # Normal SSH connection with Ghostty terminfo available
env "${env_vars[@]}" ssh "$@"
builtin return 0
fi
echo "Terminfo installation failed. Using basic integration." >&2
fi
-
+
# Fallback to basic integration
_ghostty_ssh_basic "$@"
}