ghostty/src/shell-integration/elvish/lib/ghostty-integration.elv
Jon Parise e522d54d7b shell-integration: simplify "ssh target" checks
This value is always set to a non-empty string, and we only need this
value after we've determined that 'ssh_hostname' is non-empty.

In bash and zsh, we also don't need to check for the 'ghostty' command
before we attempt to add the target to the cache. That command will
safely fail silently if it's not available.
2025-07-09 15:59:59 -04:00

214 lines
7.1 KiB
Plaintext

{
fn restore-xdg-dirs {
use str
var integration-dir = $E:GHOSTTY_SHELL_INTEGRATION_XDG_DIR
var xdg-dirs = [(str:split ':' $E:XDG_DATA_DIRS)]
var len = (count $xdg-dirs)
var index = $nil
range $len | each {|dir-index|
if (eq $xdg-dirs[$dir-index] $integration-dir) {
set index = $dir-index
break
}
}
if (eq $nil $index) { return } # will appear as an error
if (== 0 $index) {
set xdg-dirs = $xdg-dirs[1..]
} elif (== (- $len 1) $index) {
set xdg-dirs = $xdg-dirs[0..(- $len 1)]
} else {
# no builtin function for this : )
set xdg-dirs = [ (take $index $xdg-dirs) (drop (+ 1 $index) $xdg-dirs) ]
}
if (== 0 (count $xdg-dirs)) {
unset-env XDG_DATA_DIRS
} else {
set-env XDG_DATA_DIRS (str:join ':' $xdg-dirs)
}
unset-env GHOSTTY_SHELL_INTEGRATION_XDG_DIR
}
if (and (has-env GHOSTTY_SHELL_INTEGRATION_XDG_DIR) (has-env XDG_DATA_DIRS)) {
restore-xdg-dirs
}
}
{
use str
# helper used by `mark-*` functions
fn set-prompt-state {|new| set-env __ghostty_prompt_state $new }
fn mark-prompt-start {
if (not-eq prompt-start (constantly $E:__ghostty_prompt_state)) {
printf "\e]133;D\a"
}
set-prompt-state 'prompt-start'
printf "\e]133;A\a"
}
fn mark-output-start {|_|
set-prompt-state 'pre-exec'
printf "\e]133;C\a"
}
fn mark-output-end {|cmd-info|
set-prompt-state 'post-exec'
var exit-status = 0
# in case of error: retrieve exit status,
# unless does not exist (= builtin function failure), then default to 1
if (not-eq $nil $cmd-info[error]) {
set exit-status = 1
if (has-key $cmd-info[error] reason) {
if (has-key $cmd-info[error][reason] exit-status) {
set exit-status = $cmd-info[error][reason][exit-status]
}
}
}
printf "\e]133;D;"$exit-status"\a"
}
fn report-pwd {
use platform
printf "\e]7;kitty-shell-cwd://%s%s\a" (platform:hostname) $pwd
}
fn sudo-with-terminfo {|@args|
var sudoedit = $false
for arg $args {
use str
if (str:has-prefix $arg -) {
if (has-value [e -edit] $arg[1..]) {
set sudoedit = $true
break
}
continue
}
if (not (has-value $arg =)) { break }
}
if (not $sudoedit) { set args = [ TERMINFO=$E:TERMINFO $@args ] }
(external sudo) $@args
}
# SSH Integration
use str
if (str:contains $E:GHOSTTY_SHELL_FEATURES ssh-) {
fn ssh {|@args|
var ssh-term = "xterm-256color"
var ssh-opts = []
# Configure environment variables for remote session
if (str:contains $E:GHOSTTY_SHELL_FEATURES ssh-env) {
set ssh-opts = (conj $ssh-opts
-o "SetEnv COLORTERM=truecolor"
-o "SendEnv TERM_PROGRAM TERM_PROGRAM_VERSION"
)
}
# Install terminfo on remote host if needed
if (str:contains $E:GHOSTTY_SHELL_FEATURES ssh-terminfo) {
var ssh-user = ""
var ssh-hostname = ""
# Parse ssh config
var ssh-config = (external ssh -G $@args 2>/dev/null | slurp)
for line (str:split "\n" $ssh-config) {
var parts = (str:split " " $line)
if (> (count $parts) 1) {
var ssh-key = $parts[0]
var ssh-value = $parts[1]
if (eq $ssh-key user) {
set ssh-user = $ssh-value
} elif (eq $ssh-key hostname) {
set ssh-hostname = $ssh-value
}
if (and (not-eq $ssh-user "") (not-eq $ssh-hostname "")) {
break
}
}
}
if (not-eq $ssh-hostname "") {
var ssh-target = $ssh-user"@"$ssh-hostname
# Check if terminfo is already cached
if (and (has-external ghostty) (bool ?(external ghostty +ssh-cache --host=$ssh-target >/dev/null 2>&1))) {
set ssh-term = "xterm-ghostty"
} elif (has-external infocmp) {
var ssh-terminfo = (external infocmp -0 -x xterm-ghostty 2>/dev/null | slurp)
if (not-eq $ssh-terminfo "") {
echo "Setting up xterm-ghostty terminfo on "$ssh-hostname"..." >&2
var ssh-cpath-dir = ""
try {
set ssh-cpath-dir = (external mktemp -d "/tmp/ghostty-ssh-"$ssh-user".XXXXXX" 2>/dev/null | slurp)
} catch {
set ssh-cpath-dir = "/tmp/ghostty-ssh-"$ssh-user"."(randint 10000 99999)
}
var ssh-cpath = $ssh-cpath-dir"/socket"
if (bool ?(echo $ssh-terminfo | external ssh $@ssh-opts -o ControlMaster=yes -o ControlPath=$ssh-cpath -o ControlPersist=60s $@args '
infocmp xterm-ghostty >/dev/null 2>&1 && exit 0
command -v tic >/dev/null 2>&1 || exit 1
mkdir -p ~/.terminfo 2>/dev/null && tic -x - 2>/dev/null && exit 0
exit 1
' 2>/dev/null)) {
set ssh-term = "xterm-ghostty"
set ssh-opts = (conj $ssh-opts -o ControlPath=$ssh-cpath)
# Cache successful installation
if (has-external ghostty) {
external ghostty +ssh-cache --add=$ssh-target >/dev/null 2>&1
}
} else {
echo "Warning: Failed to install terminfo." >&2
}
} else {
echo "Warning: Could not generate terminfo data." >&2
}
} else {
echo "Warning: ghostty command not available for cache management." >&2
}
}
}
# Execute SSH with TERM environment variable
external E:TERM=$ssh-term ssh $@ssh-opts $@args
}
}
defer {
mark-prompt-start
report-pwd
}
set edit:before-readline = (conj $edit:before-readline $mark-prompt-start~)
set edit:after-readline = (conj $edit:after-readline $mark-output-start~)
set edit:after-command = (conj $edit:after-command $mark-output-end~)
var features = [(str:split ',' $E:GHOSTTY_SHELL_FEATURES)]
if (has-value $features title) {
set after-chdir = (conj $after-chdir {|_| report-pwd })
}
if (has-value $features cursor) {
fn beam { printf "\e[5 q" }
fn block { printf "\e[0 q" }
set edit:before-readline = (conj $edit:before-readline $beam~)
set edit:after-readline = (conj $edit:after-readline {|_| block })
}
if (and (has-value $features sudo) (not-eq "" $E:TERMINFO) (has-external sudo)) {
edit:add-var sudo~ $sudo-with-terminfo~
}
}