mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-15 08:16:13 +03:00
refactor: replace ghostty wrapper with proper CLI actions for terminfo cache management
- Add +list-ssh-cache and +clear-ssh-cache CLI actions - Remove ghostty() wrapper functions from all shell integrations - Improve variable naming in shell scripts for readability Addresses @00-kat's feedback about CLI discoverability and naming consistency. The new CLI actions follow established Ghostty patterns and are discoverable via `ghostty --help`, while maintaining clean separation of concerns between shell logic and cache management.
This commit is contained in:
@ -9,6 +9,8 @@ const list_keybinds = @import("list_keybinds.zig");
|
|||||||
const list_themes = @import("list_themes.zig");
|
const list_themes = @import("list_themes.zig");
|
||||||
const list_colors = @import("list_colors.zig");
|
const list_colors = @import("list_colors.zig");
|
||||||
const list_actions = @import("list_actions.zig");
|
const list_actions = @import("list_actions.zig");
|
||||||
|
const list_ssh_cache = @import("list_ssh_cache.zig");
|
||||||
|
const clear_ssh_cache = @import("clear_ssh_cache.zig");
|
||||||
const edit_config = @import("edit_config.zig");
|
const edit_config = @import("edit_config.zig");
|
||||||
const show_config = @import("show_config.zig");
|
const show_config = @import("show_config.zig");
|
||||||
const validate_config = @import("validate_config.zig");
|
const validate_config = @import("validate_config.zig");
|
||||||
@ -41,6 +43,12 @@ pub const Action = enum {
|
|||||||
/// List keybind actions
|
/// List keybind actions
|
||||||
@"list-actions",
|
@"list-actions",
|
||||||
|
|
||||||
|
/// List hosts with Ghostty SSH terminfo installed
|
||||||
|
@"list-ssh-cache",
|
||||||
|
|
||||||
|
/// Clear Ghostty SSH terminfo cache
|
||||||
|
@"clear-ssh-cache",
|
||||||
|
|
||||||
/// Edit the config file in the configured terminal editor.
|
/// Edit the config file in the configured terminal editor.
|
||||||
@"edit-config",
|
@"edit-config",
|
||||||
|
|
||||||
@ -155,6 +163,8 @@ pub const Action = enum {
|
|||||||
.@"list-themes" => try list_themes.run(alloc),
|
.@"list-themes" => try list_themes.run(alloc),
|
||||||
.@"list-colors" => try list_colors.run(alloc),
|
.@"list-colors" => try list_colors.run(alloc),
|
||||||
.@"list-actions" => try list_actions.run(alloc),
|
.@"list-actions" => try list_actions.run(alloc),
|
||||||
|
.@"list-ssh-cache" => @import("list_ssh_cache.zig").run(alloc),
|
||||||
|
.@"clear-ssh-cache" => @import("clear_ssh_cache.zig").run(alloc),
|
||||||
.@"edit-config" => try edit_config.run(alloc),
|
.@"edit-config" => try edit_config.run(alloc),
|
||||||
.@"show-config" => try show_config.run(alloc),
|
.@"show-config" => try show_config.run(alloc),
|
||||||
.@"validate-config" => try validate_config.run(alloc),
|
.@"validate-config" => try validate_config.run(alloc),
|
||||||
@ -192,6 +202,8 @@ pub const Action = enum {
|
|||||||
.@"list-themes" => list_themes.Options,
|
.@"list-themes" => list_themes.Options,
|
||||||
.@"list-colors" => list_colors.Options,
|
.@"list-colors" => list_colors.Options,
|
||||||
.@"list-actions" => list_actions.Options,
|
.@"list-actions" => list_actions.Options,
|
||||||
|
.@"list-ssh-cache" => list_ssh_cache.Options,
|
||||||
|
.@"clear-ssh-cache" => clear_ssh_cache.Options,
|
||||||
.@"edit-config" => edit_config.Options,
|
.@"edit-config" => edit_config.Options,
|
||||||
.@"show-config" => show_config.Options,
|
.@"show-config" => show_config.Options,
|
||||||
.@"validate-config" => validate_config.Options,
|
.@"validate-config" => validate_config.Options,
|
||||||
|
40
src/cli/clear_ssh_cache.zig
Normal file
40
src/cli/clear_ssh_cache.zig
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const args = @import("args.zig");
|
||||||
|
const Action = @import("action.zig").Action;
|
||||||
|
const ssh_cache = @import("ssh_cache.zig");
|
||||||
|
|
||||||
|
pub const Options = struct {
|
||||||
|
pub fn deinit(self: Options) void {
|
||||||
|
_ = self;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enables `-h` and `--help` to work.
|
||||||
|
pub fn help(self: Options) !void {
|
||||||
|
_ = self;
|
||||||
|
return Action.help_error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Clear the Ghostty SSH terminfo cache.
|
||||||
|
///
|
||||||
|
/// This command removes the cache of hosts where Ghostty's terminfo has been installed
|
||||||
|
/// via the ssh-terminfo shell integration feature. After clearing, terminfo will be
|
||||||
|
/// reinstalled on the next SSH connection to previously cached hosts.
|
||||||
|
///
|
||||||
|
/// Use this if you need to force reinstallation of terminfo or clean up old entries.
|
||||||
|
pub fn run(alloc: Allocator) !u8 {
|
||||||
|
var opts: Options = .{};
|
||||||
|
defer opts.deinit();
|
||||||
|
|
||||||
|
{
|
||||||
|
var iter = try args.argsIterator(alloc);
|
||||||
|
defer iter.deinit();
|
||||||
|
try args.parse(Options, alloc, &opts, &iter);
|
||||||
|
}
|
||||||
|
|
||||||
|
const stdout = std.io.getStdOut().writer();
|
||||||
|
try ssh_cache.clearCache(alloc, stdout);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
38
src/cli/list_ssh_cache.zig
Normal file
38
src/cli/list_ssh_cache.zig
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const args = @import("args.zig");
|
||||||
|
const Action = @import("action.zig").Action;
|
||||||
|
const ssh_cache = @import("ssh_cache.zig");
|
||||||
|
|
||||||
|
pub const Options = struct {
|
||||||
|
pub fn deinit(self: Options) void {
|
||||||
|
_ = self;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enables `-h` and `--help` to work.
|
||||||
|
pub fn help(self: Options) !void {
|
||||||
|
_ = self;
|
||||||
|
return Action.help_error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// List hosts with Ghostty SSH terminfo installed via the ssh-terminfo shell integration feature.
|
||||||
|
///
|
||||||
|
/// This command shows all remote hosts where Ghostty's terminfo has been successfully
|
||||||
|
/// installed through the SSH integration. The cache is automatically maintained when
|
||||||
|
/// connecting to remote hosts with `shell-integration-features = ssh-terminfo` enabled.
|
||||||
|
pub fn run(alloc: Allocator) !u8 {
|
||||||
|
var opts: Options = .{};
|
||||||
|
defer opts.deinit();
|
||||||
|
|
||||||
|
{
|
||||||
|
var iter = try args.argsIterator(alloc);
|
||||||
|
defer iter.deinit();
|
||||||
|
try args.parse(Options, alloc, &opts, &iter);
|
||||||
|
}
|
||||||
|
|
||||||
|
const stdout = std.io.getStdOut().writer();
|
||||||
|
try ssh_cache.listCachedHosts(alloc, stdout);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
71
src/cli/ssh_cache.zig
Normal file
71
src/cli/ssh_cache.zig
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const Child = std.process.Child;
|
||||||
|
|
||||||
|
/// Get the path to the shared cache script
|
||||||
|
fn getCacheScriptPath(alloc: Allocator) ![]u8 {
|
||||||
|
// Use GHOSTTY_RESOURCES_DIR if available, otherwise assume relative path
|
||||||
|
const resources_dir = std.process.getEnvVarOwned(alloc, "GHOSTTY_RESOURCES_DIR") catch {
|
||||||
|
// Fallback: assume we're running from build directory
|
||||||
|
return try alloc.dupe(u8, "src/shell-integration/shared/ghostty-ssh-cache");
|
||||||
|
};
|
||||||
|
defer alloc.free(resources_dir);
|
||||||
|
|
||||||
|
return try std.fs.path.join(alloc, &[_][]const u8{ resources_dir, "shell-integration", "shared", "ghostty-ssh-cache" });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// List cached hosts by calling the external script
|
||||||
|
pub fn listCachedHosts(alloc: Allocator, writer: anytype) !void {
|
||||||
|
const script_path = try getCacheScriptPath(alloc);
|
||||||
|
defer alloc.free(script_path);
|
||||||
|
|
||||||
|
var child = Child.init(&[_][]const u8{ script_path, "list" }, alloc);
|
||||||
|
child.stdout_behavior = .Pipe;
|
||||||
|
child.stderr_behavior = .Pipe;
|
||||||
|
|
||||||
|
try child.spawn();
|
||||||
|
|
||||||
|
const stdout = try child.stdout.?.readToEndAlloc(alloc, std.math.maxInt(usize));
|
||||||
|
defer alloc.free(stdout);
|
||||||
|
|
||||||
|
const stderr = try child.stderr.?.readToEndAlloc(alloc, std.math.maxInt(usize));
|
||||||
|
defer alloc.free(stderr);
|
||||||
|
|
||||||
|
_ = try child.wait();
|
||||||
|
|
||||||
|
// Output the results regardless of exit code
|
||||||
|
try writer.writeAll(stdout);
|
||||||
|
if (stderr.len > 0) {
|
||||||
|
try writer.writeAll(stderr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Script handles its own success/error messaging, so we don't need to check exit code
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clear cache by calling the external script
|
||||||
|
pub fn clearCache(alloc: Allocator, writer: anytype) !void {
|
||||||
|
const script_path = try getCacheScriptPath(alloc);
|
||||||
|
defer alloc.free(script_path);
|
||||||
|
|
||||||
|
var child = Child.init(&[_][]const u8{ script_path, "clear" }, alloc);
|
||||||
|
child.stdout_behavior = .Pipe;
|
||||||
|
child.stderr_behavior = .Pipe;
|
||||||
|
|
||||||
|
try child.spawn();
|
||||||
|
|
||||||
|
const stdout = try child.stdout.?.readToEndAlloc(alloc, std.math.maxInt(usize));
|
||||||
|
defer alloc.free(stdout);
|
||||||
|
|
||||||
|
const stderr = try child.stderr.?.readToEndAlloc(alloc, std.math.maxInt(usize));
|
||||||
|
defer alloc.free(stderr);
|
||||||
|
|
||||||
|
_ = try child.wait();
|
||||||
|
|
||||||
|
// Output the results regardless of exit code
|
||||||
|
try writer.writeAll(stdout);
|
||||||
|
if (stderr.len > 0) {
|
||||||
|
try writer.writeAll(stderr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Script handles its own success/error messaging, so we don't need to check exit code
|
||||||
|
}
|
@ -100,19 +100,11 @@ if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-(env|terminfo) ]]; then
|
|||||||
|
|
||||||
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-terminfo ]]; then
|
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-terminfo ]]; then
|
||||||
readonly _CACHE="${GHOSTTY_RESOURCES_DIR}/shell-integration/shared/ghostty-ssh-cache"
|
readonly _CACHE="${GHOSTTY_RESOURCES_DIR}/shell-integration/shared/ghostty-ssh-cache"
|
||||||
# If 'ssh-terminfo' flag is enabled, wrap ghostty to provide cache management commands
|
|
||||||
ghostty() {
|
|
||||||
case "$1" in
|
|
||||||
ssh-cache-list) "$_CACHE" list ;;
|
|
||||||
ssh-cache-clear) "$_CACHE" clear ;;
|
|
||||||
*) builtin command ghostty "$@" ;;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# SSH wrapper
|
# SSH wrapper
|
||||||
ssh() {
|
ssh() {
|
||||||
local e=() o=() c=() # Removed 't' from here
|
local env=() opts=() ctrl=()
|
||||||
|
|
||||||
# Set up env vars first so terminfo installation inherits them
|
# Set up env vars first so terminfo installation inherits them
|
||||||
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-env ]]; then
|
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-env ]]; then
|
||||||
@ -123,35 +115,35 @@ if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-(env|terminfo) ]]; then
|
|||||||
)
|
)
|
||||||
for v in "${vars[@]}"; do
|
for v in "${vars[@]}"; do
|
||||||
builtin export "${v?}"
|
builtin export "${v?}"
|
||||||
o+=(-o "SendEnv ${v%=*}" -o "SetEnv $v")
|
opts+=(-o "SendEnv ${v%=*}" -o "SetEnv $v")
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Install terminfo if needed, reuse control connection for main session
|
# Install terminfo if needed, reuse control connection for main session
|
||||||
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-terminfo ]]; then
|
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-terminfo ]]; then
|
||||||
# Get target (only when needed for terminfo)
|
# Get target (only when needed for terminfo)
|
||||||
builtin local t
|
builtin local target
|
||||||
t=$(builtin command ssh -G "$@" 2>/dev/null | awk '/^(user|hostname) /{print $2}' | paste -sd'@')
|
target=$(builtin command ssh -G "$@" 2>/dev/null | awk '/^(user|hostname) /{print $2}' | paste -sd'@')
|
||||||
|
|
||||||
if [[ -n "$t" ]] && "$_CACHE" chk "$t"; then
|
if [[ -n "$target" ]] && "$_CACHE" chk "$target"; then
|
||||||
e+=(TERM=xterm-ghostty)
|
env+=(TERM=xterm-ghostty)
|
||||||
elif builtin command -v infocmp >/dev/null 2>&1; then
|
elif builtin command -v infocmp >/dev/null 2>&1; then
|
||||||
builtin local ti
|
builtin local tinfo
|
||||||
ti=$(infocmp -x xterm-ghostty 2>/dev/null) || builtin echo "Warning: xterm-ghostty terminfo not found locally." >&2
|
tinfo=$(infocmp -x xterm-ghostty 2>/dev/null) || builtin echo "Warning: xterm-ghostty terminfo not found locally." >&2
|
||||||
if [[ -n "$ti" ]]; then
|
if [[ -n "$tinfo" ]]; then
|
||||||
builtin echo "Setting up Ghostty terminfo on remote host..." >&2
|
builtin echo "Setting up Ghostty terminfo on remote host..." >&2
|
||||||
builtin local cp
|
builtin local cpath
|
||||||
cp="/tmp/ghostty-ssh-$USER-$RANDOM-$(date +%s)"
|
cpath="/tmp/ghostty-ssh-$USER-$RANDOM-$(date +%s)"
|
||||||
case $(builtin echo "$ti" | builtin command ssh "${o[@]}" -o ControlMaster=yes -o ControlPath="$cp" -o ControlPersist=60s "$@" '
|
case $(builtin echo "$tinfo" | builtin command ssh "${opts[@]}" -o ControlMaster=yes -o ControlPath="$cpath" -o ControlPersist=60s "$@" '
|
||||||
infocmp xterm-ghostty >/dev/null 2>&1 && echo OK && exit
|
infocmp xterm-ghostty >/dev/null 2>&1 && echo OK && exit
|
||||||
command -v tic >/dev/null 2>&1 || { echo NO_TIC; exit 1; }
|
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
|
mkdir -p ~/.terminfo 2>/dev/null && tic -x - 2>/dev/null && echo OK || echo FAIL
|
||||||
') in
|
') in
|
||||||
OK)
|
OK)
|
||||||
builtin echo "Terminfo setup complete." >&2
|
builtin echo "Terminfo setup complete." >&2
|
||||||
[[ -n "$t" ]] && "$_CACHE" add "$t"
|
[[ -n "$target" ]] && "$_CACHE" add "$target"
|
||||||
e+=(TERM=xterm-ghostty)
|
env+=(TERM=xterm-ghostty)
|
||||||
c+=(-o "ControlPath=$cp")
|
ctrl+=(-o "ControlPath=$cpath")
|
||||||
;;
|
;;
|
||||||
*) builtin echo "Warning: Failed to install terminfo." >&2 ;;
|
*) builtin echo "Warning: Failed to install terminfo." >&2 ;;
|
||||||
esac
|
esac
|
||||||
@ -163,14 +155,14 @@ if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-(env|terminfo) ]]; then
|
|||||||
|
|
||||||
# Fallback TERM only if terminfo didn't set it
|
# Fallback TERM only if terminfo didn't set it
|
||||||
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-env ]]; then
|
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-env ]]; then
|
||||||
[[ $TERM == xterm-ghostty && ! " ${e[*]} " =~ " TERM=" ]] && e+=(TERM=xterm-256color)
|
[[ $TERM == xterm-ghostty && ! " ${env[*]} " =~ " TERM=" ]] && env+=(TERM=xterm-256color)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Execute
|
# Execute
|
||||||
if [[ ${#e[@]} -gt 0 ]]; then
|
if [[ ${#env[@]} -gt 0 ]]; then
|
||||||
env "${e[@]}" ssh "${o[@]}" "${c[@]}" "$@"
|
env "${env[@]}" ssh "${opts[@]}" "${ctrl[@]}" "$@"
|
||||||
else
|
else
|
||||||
builtin command ssh "${o[@]}" "${c[@]}" "$@"
|
builtin command ssh "${opts[@]}" "${ctrl[@]}" "$@"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
fi
|
fi
|
||||||
|
@ -100,85 +100,68 @@
|
|||||||
|
|
||||||
# SSH Integration
|
# SSH Integration
|
||||||
use str
|
use str
|
||||||
use path
|
|
||||||
use re
|
|
||||||
|
|
||||||
if (re:match 'ssh-(env|terminfo)' $E:GHOSTTY_SHELL_FEATURES) {
|
if (str:contains $E:GHOSTTY_SHELL_FEATURES ssh-env) or (str:contains $E:GHOSTTY_SHELL_FEATURES ssh-terminfo) {
|
||||||
if (re:match 'ssh-terminfo' $E:GHOSTTY_SHELL_FEATURES) {
|
|
||||||
var _cache_script = (path:join $E:GHOSTTY_RESOURCES_DIR shell-integration shared ghostty-ssh-cache)
|
|
||||||
|
|
||||||
# Wrap ghostty command to provide cache management commands
|
|
||||||
fn ghostty {|@args|
|
|
||||||
if (eq $args[0] ssh-cache-list) {
|
|
||||||
(external $_cache_script) list
|
|
||||||
} elif (eq $args[0] ssh-cache-clear) {
|
|
||||||
(external $_cache_script) clear
|
|
||||||
} else {
|
|
||||||
(external ghostty) $@args
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
edit:add-var ghostty~ $ghostty~
|
if (str:contains $E:GHOSTTY_SHELL_FEATURES ssh-terminfo) {
|
||||||
|
var _CACHE = $E:GHOSTTY_RESOURCES_DIR/shell-integration/shared/ghostty-ssh-cache
|
||||||
}
|
}
|
||||||
|
|
||||||
# SSH wrapper
|
# SSH wrapper
|
||||||
fn ssh {|@args|
|
fn ssh {|@args|
|
||||||
var e = []
|
var env = []
|
||||||
var o = []
|
var opts = []
|
||||||
var c = []
|
var ctrl = []
|
||||||
|
|
||||||
# Set up env vars first so terminfo installation inherits them
|
# Set up env vars first so terminfo installation inherits them
|
||||||
if (re:match 'ssh-env' $E:GHOSTTY_SHELL_FEATURES) {
|
if (str:contains $E:GHOSTTY_SHELL_FEATURES ssh-env) {
|
||||||
set-env COLORTERM (or $E:COLORTERM truecolor)
|
var vars = [
|
||||||
set-env TERM_PROGRAM (or $E:TERM_PROGRAM ghostty)
|
COLORTERM=truecolor
|
||||||
if (has-env GHOSTTY_VERSION) {
|
TERM_PROGRAM=ghostty
|
||||||
set-env TERM_PROGRAM_VERSION $E:GHOSTTY_VERSION
|
]
|
||||||
}
|
if (not-eq $E:GHOSTTY_VERSION '') {
|
||||||
|
|
||||||
var vars = [COLORTERM=truecolor TERM_PROGRAM=ghostty]
|
|
||||||
if (has-env GHOSTTY_VERSION) {
|
|
||||||
set vars = [$@vars TERM_PROGRAM_VERSION=$E:GHOSTTY_VERSION]
|
set vars = [$@vars TERM_PROGRAM_VERSION=$E:GHOSTTY_VERSION]
|
||||||
}
|
}
|
||||||
|
|
||||||
for v $vars {
|
for v $vars {
|
||||||
var varname = (str:split &max=2 '=' $v | take 1)
|
set-env (str:split = $v | take 1) (str:split = $v | drop 1 | str:join =)
|
||||||
set o = [$@o -o "SendEnv "$varname -o "SetEnv "$v]
|
var varname = (str:split = $v | take 1)
|
||||||
|
set opts = [$@opts -o 'SendEnv '$varname -o 'SetEnv '$v]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# Install terminfo if needed, reuse control connection for main session
|
# Install terminfo if needed, reuse control connection for main session
|
||||||
if (re:match 'ssh-terminfo' $E:GHOSTTY_SHELL_FEATURES) {
|
if (str:contains $E:GHOSTTY_SHELL_FEATURES ssh-terminfo) {
|
||||||
# Get target (only when needed for terminfo)
|
# Get target
|
||||||
var t = ""
|
var target = ''
|
||||||
try {
|
try {
|
||||||
set t = (e:ssh -G $@args 2>/dev/null | awk '/^(user|hostname) /{print $2}' | paste -sd'@' | str:trim-space)
|
set target = (e:ssh -G $@args 2>/dev/null | e:awk '/^(user|hostname) /{print $2}' | e:paste -sd'@')
|
||||||
} catch e {
|
} catch { }
|
||||||
# Ignore errors
|
|
||||||
}
|
|
||||||
|
|
||||||
if (and (not-eq $t "") (try { (external $_cache_script) chk $t } catch e { put $false })) {
|
if (and (not-eq $target '') ($_CACHE chk $target)) {
|
||||||
set e = [$@e TERM=xterm-ghostty]
|
set env = [$@env TERM=xterm-ghostty]
|
||||||
} elif (has-external infocmp) {
|
} elif (has-external infocmp) {
|
||||||
var ti = ""
|
var tinfo = ''
|
||||||
try {
|
try {
|
||||||
set ti = (infocmp -x xterm-ghostty 2>/dev/null | slurp)
|
set tinfo = (e:infocmp -x xterm-ghostty 2>/dev/null)
|
||||||
} catch e {
|
} catch {
|
||||||
echo "Warning: xterm-ghostty terminfo not found locally." >&2
|
echo "Warning: xterm-ghostty terminfo not found locally." >&2
|
||||||
}
|
}
|
||||||
if (not-eq $ti "") {
|
|
||||||
|
if (not-eq $tinfo '') {
|
||||||
echo "Setting up Ghostty terminfo on remote host..." >&2
|
echo "Setting up Ghostty terminfo on remote host..." >&2
|
||||||
var cp = "/tmp/ghostty-ssh-"$E:USER"-"(randint 10000)"-"(date +%s | str:trim-space)
|
var cpath = '/tmp/ghostty-ssh-'$E:USER'-'(randint 0 32767)'-'(date +%s)
|
||||||
var result = (echo $ti | e:ssh $@o -o ControlMaster=yes -o ControlPath=$cp -o ControlPersist=60s $@args '
|
var result = (echo $tinfo | e:ssh $@opts -o ControlMaster=yes -o ControlPath=$cpath -o ControlPersist=60s $@args '
|
||||||
infocmp xterm-ghostty >/dev/null 2>&1 && echo OK && exit
|
infocmp xterm-ghostty >/dev/null 2>&1 && echo OK && exit
|
||||||
command -v tic >/dev/null 2>&1 || { echo NO_TIC; exit 1; }
|
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
|
mkdir -p ~/.terminfo 2>/dev/null && tic -x - 2>/dev/null && echo OK || echo FAIL
|
||||||
' | str:trim-space)
|
')
|
||||||
|
|
||||||
if (eq $result OK) {
|
if (eq $result OK) {
|
||||||
echo "Terminfo setup complete." >&2
|
echo "Terminfo setup complete." >&2
|
||||||
if (not-eq $t "") {
|
if (not-eq $target '') { $_CACHE add $target }
|
||||||
(external $_cache_script) add $t
|
set env = [$@env TERM=xterm-ghostty]
|
||||||
}
|
set ctrl = [$@ctrl -o ControlPath=$cpath]
|
||||||
set e = [$@e TERM=xterm-ghostty]
|
|
||||||
set c = [$@c -o ControlPath=$cp]
|
|
||||||
} else {
|
} else {
|
||||||
echo "Warning: Failed to install terminfo." >&2
|
echo "Warning: Failed to install terminfo." >&2
|
||||||
}
|
}
|
||||||
@ -189,22 +172,19 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Fallback TERM only if terminfo didn't set it
|
# Fallback TERM only if terminfo didn't set it
|
||||||
if (re:match 'ssh-env' $E:GHOSTTY_SHELL_FEATURES) {
|
if (str:contains $E:GHOSTTY_SHELL_FEATURES ssh-env) {
|
||||||
if (and (eq $E:TERM xterm-ghostty) (not (re:match 'TERM=' (str:join ' ' $e)))) {
|
if (and (eq $E:TERM xterm-ghostty) (not (str:contains (str:join ' ' $env) 'TERM='))) {
|
||||||
set e = [$@e TERM=xterm-256color]
|
set env = [$@env TERM=xterm-256color]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# Execute
|
# Execute
|
||||||
if (> (count $e) 0) {
|
if (> (count $env) 0) {
|
||||||
e:env $@e e:ssh $@o $@c $@args
|
e:env $@env e:ssh $@opts $@ctrl $@args
|
||||||
} else {
|
} else {
|
||||||
e:ssh $@o $@c $@args
|
e:ssh $@opts $@ctrl $@args
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# Export ssh function for global use
|
|
||||||
set edit:add-var[ssh] = $ssh~
|
|
||||||
}
|
}
|
||||||
|
|
||||||
defer {
|
defer {
|
||||||
|
@ -87,89 +87,86 @@ function __ghostty_setup --on-event fish_prompt -d "Setup ghostty integration"
|
|||||||
end
|
end
|
||||||
|
|
||||||
# SSH Integration
|
# SSH Integration
|
||||||
if string match -qr 'ssh-(env|terminfo)' "$GHOSTTY_SHELL_FEATURES"
|
if string match -qr 'ssh-(env|terminfo)' $GHOSTTY_SHELL_FEATURES
|
||||||
if string match -qr ssh-terminfo "$GHOSTTY_SHELL_FEATURES"
|
|
||||||
set -g _cache_script "$GHOSTTY_RESOURCES_DIR/shell-integration/shared/ghostty-ssh-cache"
|
|
||||||
|
|
||||||
# Wrap ghostty command to provide cache management commands
|
if string match -q '*ssh-terminfo*' $GHOSTTY_SHELL_FEATURES
|
||||||
function ghostty -d "Wrap ghostty to provide cache management commands"
|
set -g _CACHE "$GHOSTTY_RESOURCES_DIR/shell-integration/shared/ghostty-ssh-cache"
|
||||||
switch "$argv[1]"
|
|
||||||
case ssh-cache-list
|
|
||||||
command "$_cache_script" list
|
|
||||||
case ssh-cache-clear
|
|
||||||
command "$_cache_script" clear
|
|
||||||
case "*"
|
|
||||||
command ghostty $argv
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# SSH wrapper
|
# SSH wrapper
|
||||||
function ssh
|
function ssh
|
||||||
set -l e
|
set -l env
|
||||||
set -l o
|
set -l opts
|
||||||
set -l c
|
set -l ctrl
|
||||||
|
|
||||||
# Set up env vars first so terminfo installation inherits them
|
# Set up env vars first so terminfo installation inherits them
|
||||||
if string match -qr ssh-env "$GHOSTTY_SHELL_FEATURES"
|
if string match -q '*ssh-env*' $GHOSTTY_SHELL_FEATURES
|
||||||
set -gx COLORTERM (test -n "$COLORTERM" && echo "$COLORTERM" || echo "truecolor")
|
set -l vars \
|
||||||
set -gx TERM_PROGRAM (test -n "$TERM_PROGRAM" && echo "$TERM_PROGRAM" || echo "ghostty")
|
COLORTERM=truecolor \
|
||||||
test -n "$GHOSTTY_VERSION" && set -gx TERM_PROGRAM_VERSION "$GHOSTTY_VERSION"
|
TERM_PROGRAM=ghostty
|
||||||
|
|
||||||
set -l vars COLORTERM=truecolor TERM_PROGRAM=ghostty
|
if test -n "$GHOSTTY_VERSION"
|
||||||
test -n "$GHOSTTY_VERSION" && set vars $vars "TERM_PROGRAM_VERSION=$GHOSTTY_VERSION"
|
set -a vars "TERM_PROGRAM_VERSION=$GHOSTTY_VERSION"
|
||||||
|
end
|
||||||
|
|
||||||
for v in $vars
|
for v in $vars
|
||||||
set -l varname (string split -m1 '=' "$v")[1]
|
set -l parts (string split = $v)
|
||||||
set o $o -o "SendEnv $varname" -o "SetEnv $v"
|
set -gx $parts[1] $parts[2]
|
||||||
|
set -a opts -o "SendEnv $parts[1]" -o "SetEnv $v"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Install terminfo if needed, reuse control connection for main session
|
# Install terminfo if needed, reuse control connection for main session
|
||||||
if string match -qr ssh-terminfo "$GHOSTTY_SHELL_FEATURES"
|
if string match -q '*ssh-terminfo*' $GHOSTTY_SHELL_FEATURES
|
||||||
# Get target (only when needed for terminfo)
|
# Get target
|
||||||
set -l t (builtin command ssh -G $argv 2>/dev/null | awk '/^(user|hostname) /{print $2}' | paste -sd'@')
|
set -l target (command ssh -G $argv 2>/dev/null | awk '/^(user|hostname) /{print $2}' | paste -sd'@')
|
||||||
|
|
||||||
if test -n "$t" && command "$_cache_script" chk "$t"
|
if test -n "$target" -a ("$_CACHE" chk "$target")
|
||||||
set e $e TERM=xterm-ghostty
|
set -a env TERM=xterm-ghostty
|
||||||
else if command -v infocmp >/dev/null 2>&1
|
else if command -v infocmp >/dev/null 2>&1
|
||||||
set -l ti
|
set -l tinfo (infocmp -x xterm-ghostty 2>/dev/null)
|
||||||
set ti (infocmp -x xterm-ghostty 2>/dev/null) || builtin echo "Warning: xterm-ghostty terminfo not found locally." >&2
|
set -l status_code $status
|
||||||
if test -n "$ti"
|
|
||||||
builtin echo "Setting up Ghostty terminfo on remote host..." >&2
|
if test $status_code -ne 0
|
||||||
set -l cp "/tmp/ghostty-ssh-$USER-"(random)"-"(date +%s)
|
echo "Warning: xterm-ghostty terminfo not found locally." >&2
|
||||||
set -l result (builtin echo "$ti" | builtin command ssh $o -o ControlMaster=yes -o ControlPath="$cp" -o ControlPersist=60s $argv '
|
end
|
||||||
|
|
||||||
|
if test -n "$tinfo"
|
||||||
|
echo "Setting up Ghostty terminfo on remote host..." >&2
|
||||||
|
set -l cpath "/tmp/ghostty-ssh-$USER-"(random)"-"(date +%s)
|
||||||
|
set -l result (echo "$tinfo" | command ssh $opts -o ControlMaster=yes -o ControlPath="$cpath" -o ControlPersist=60s $argv '
|
||||||
infocmp xterm-ghostty >/dev/null 2>&1 && echo OK && exit
|
infocmp xterm-ghostty >/dev/null 2>&1 && echo OK && exit
|
||||||
command -v tic >/dev/null 2>&1 || { echo NO_TIC; exit 1; }
|
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
|
mkdir -p ~/.terminfo 2>/dev/null && tic -x - 2>/dev/null && echo OK || echo FAIL
|
||||||
')
|
')
|
||||||
|
|
||||||
switch $result
|
switch $result
|
||||||
case OK
|
case OK
|
||||||
builtin echo "Terminfo setup complete." >&2
|
echo "Terminfo setup complete." >&2
|
||||||
test -n "$t" && command "$_cache_script" add "$t"
|
test -n "$target" && "$_CACHE" add "$target"
|
||||||
set e $e TERM=xterm-ghostty
|
set -a env TERM=xterm-ghostty
|
||||||
set c $c -o "ControlPath=$cp"
|
set -a ctrl -o "ControlPath=$cpath"
|
||||||
case '*'
|
case '*'
|
||||||
builtin echo "Warning: Failed to install terminfo." >&2
|
echo "Warning: Failed to install terminfo." >&2
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
builtin echo "Warning: infocmp not found locally. Terminfo installation unavailable." >&2
|
echo "Warning: infocmp not found locally. Terminfo installation unavailable." >&2
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Fallback TERM only if terminfo didn't set it
|
# Fallback TERM only if terminfo didn't set it
|
||||||
if string match -qr ssh-env "$GHOSTTY_SHELL_FEATURES"
|
if string match -q '*ssh-env*' $GHOSTTY_SHELL_FEATURES
|
||||||
if test "$TERM" = xterm-ghostty && not string match -q '*TERM=*' "$e"
|
if test "$TERM" = xterm-ghostty -a ! (string join ' ' $env | string match -q '*TERM=*')
|
||||||
set e $e TERM=xterm-256color
|
set -a env TERM=xterm-256color
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Execute
|
# Execute
|
||||||
if test (count $e) -gt 0
|
if test (count $env) -gt 0
|
||||||
env $e ssh $o $c $argv
|
env $env command ssh $opts $ctrl $argv
|
||||||
else
|
else
|
||||||
builtin command ssh $o $c $argv
|
command ssh $opts $ctrl $argv
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -246,80 +246,76 @@ _ghostty_deferred_init() {
|
|||||||
|
|
||||||
# SSH Integration
|
# SSH Integration
|
||||||
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-(env|terminfo) ]]; then
|
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-(env|terminfo) ]]; then
|
||||||
|
|
||||||
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-terminfo ]]; then
|
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-terminfo ]]; then
|
||||||
readonly _cache_script="${GHOSTTY_RESOURCES_DIR}/shell-integration/shared/ghostty-ssh-cache"
|
readonly _CACHE="${GHOSTTY_RESOURCES_DIR}/shell-integration/shared/ghostty-ssh-cache"
|
||||||
|
|
||||||
# Wrap ghostty command to provide cache management commands
|
|
||||||
ghostty() {
|
|
||||||
case "$1" in
|
|
||||||
ssh-cache-list) "$_cache_script" list ;;
|
|
||||||
ssh-cache-clear) "$_cache_script" clear ;;
|
|
||||||
*) builtin command ghostty "$@" ;;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# SSH wrapper
|
# SSH wrapper
|
||||||
ssh() {
|
ssh() {
|
||||||
local -a e o c
|
local -a env opts ctrl
|
||||||
|
env=()
|
||||||
|
opts=()
|
||||||
|
ctrl=()
|
||||||
|
|
||||||
# Set up env vars first so terminfo installation inherits them
|
# Set up env vars first so terminfo installation inherits them
|
||||||
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-env ]]; then
|
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-env ]]; then
|
||||||
local vars=(
|
local -a vars
|
||||||
|
vars=(
|
||||||
COLORTERM=truecolor
|
COLORTERM=truecolor
|
||||||
TERM_PROGRAM=ghostty
|
TERM_PROGRAM=ghostty
|
||||||
${GHOSTTY_VERSION:+TERM_PROGRAM_VERSION=$GHOSTTY_VERSION}
|
${GHOSTTY_VERSION:+TERM_PROGRAM_VERSION=$GHOSTTY_VERSION}
|
||||||
)
|
)
|
||||||
for v in "${vars[@]}"; do
|
for v in "${vars[@]}"; do
|
||||||
builtin export "${v?}"
|
export "${v?}"
|
||||||
o+=(-o "SendEnv ${v%=*}" -o "SetEnv $v")
|
opts+=(-o "SendEnv ${v%=*}" -o "SetEnv $v")
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Install terminfo if needed, reuse control connection for main session
|
# Install terminfo if needed, reuse control connection for main session
|
||||||
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-terminfo ]]; then
|
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-terminfo ]]; then
|
||||||
# Get target (only when needed for terminfo)
|
# Get target (only when needed for terminfo)
|
||||||
builtin local t
|
local target
|
||||||
t=$(builtin command ssh -G "$@" 2>/dev/null | awk '/^(user|hostname) /{print $2}' | paste -sd'@')
|
target=$(command ssh -G "$@" 2>/dev/null | awk '/^(user|hostname) /{print $2}' | paste -sd'@')
|
||||||
|
|
||||||
if [[ -n $t ]] && "$_cache_script" chk "$t"; then
|
if [[ -n "$target" ]] && "$_CACHE" chk "$target"; then
|
||||||
e+=(TERM=xterm-ghostty)
|
env+=(TERM=xterm-ghostty)
|
||||||
elif builtin command -v infocmp >/dev/null 2>&1; then
|
elif command -v infocmp >/dev/null 2>&1; then
|
||||||
local ti
|
local tinfo
|
||||||
ti=$(infocmp -x xterm-ghostty 2>/dev/null) || builtin echo "Warning: xterm-ghostty terminfo not found locally." >&2
|
tinfo=$(infocmp -x xterm-ghostty 2>/dev/null) || echo "Warning: xterm-ghostty terminfo not found locally." >&2
|
||||||
if [[ -n $ti ]]; then
|
if [[ -n "$tinfo" ]]; then
|
||||||
builtin echo "Setting up Ghostty terminfo on remote host..." >&2
|
echo "Setting up Ghostty terminfo on remote host..." >&2
|
||||||
local cp
|
local cpath
|
||||||
cp="/tmp/ghostty-ssh-$USER-$RANDOM-$(date +%s)"
|
cpath="/tmp/ghostty-ssh-$USER-$RANDOM-$(date +%s)"
|
||||||
case $(builtin echo "$ti" | builtin command ssh "${o[@]}" -o ControlMaster=yes -o ControlPath="$cp" -o ControlPersist=60s "$@" '
|
case $(echo "$tinfo" | command ssh "${opts[@]}" -o ControlMaster=yes -o ControlPath="$cpath" -o ControlPersist=60s "$@" '
|
||||||
infocmp xterm-ghostty >/dev/null 2>&1 && echo OK && exit
|
infocmp xterm-ghostty >/dev/null 2>&1 && echo OK && exit
|
||||||
command -v tic >/dev/null 2>&1 || { echo NO_TIC; exit 1; }
|
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
|
mkdir -p ~/.terminfo 2>/dev/null && tic -x - 2>/dev/null && echo OK || echo FAIL
|
||||||
') in
|
') in
|
||||||
OK)
|
OK)
|
||||||
builtin echo "Terminfo setup complete." >&2
|
echo "Terminfo setup complete." >&2
|
||||||
[[ -n $t ]] && "$_cache_script" add "$t"
|
[[ -n "$target" ]] && "$_CACHE" add "$target"
|
||||||
e+=(TERM=xterm-ghostty)
|
env+=(TERM=xterm-ghostty)
|
||||||
c+=(-o "ControlPath=$cp")
|
ctrl+=(-o "ControlPath=$cpath")
|
||||||
;;
|
;;
|
||||||
*) builtin echo "Warning: Failed to install terminfo." >&2 ;;
|
*) echo "Warning: Failed to install terminfo." >&2 ;;
|
||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
builtin echo "Warning: infocmp not found locally. Terminfo installation unavailable." >&2
|
echo "Warning: infocmp not found locally. Terminfo installation unavailable." >&2
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Fallback TERM only if terminfo didn't set it
|
# Fallback TERM only if terminfo didn't set it
|
||||||
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-env ]]; then
|
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-env ]]; then
|
||||||
[[ $TERM == xterm-ghostty && ! " ${(j: :)e} " =~ " TERM=" ]] && e+=(TERM=xterm-256color)
|
[[ $TERM == xterm-ghostty && ! " ${env[*]} " =~ " TERM=" ]] && env+=(TERM=xterm-256color)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Execute
|
# Execute
|
||||||
if (( ${#e} > 0 )); then
|
if [[ ${#env[@]} -gt 0 ]]; then
|
||||||
env "${e[@]}" ssh "${o[@]}" "${c[@]}" "$@"
|
env "${env[@]}" command ssh "${opts[@]}" "${ctrl[@]}" "$@"
|
||||||
else
|
else
|
||||||
builtin command ssh "${o[@]}" "${c[@]}" "$@"
|
command ssh "${opts[@]}" "${ctrl[@]}" "$@"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
fi
|
fi
|
||||||
|
Reference in New Issue
Block a user