From 597328432c86e3276ded4f1e4e344f5191f4ab38 Mon Sep 17 00:00:00 2001 From: ilk Date: Thu, 16 May 2024 20:45:14 +0300 Subject: [PATCH 1/3] feat(shell-integration): add integration for Elvish - contains `.elv` file that implements the core of Elvish integration. - does not contain routines needed for automatic integration. - stored in `./elvish/lib/...` in preparation for automatic integration: Elvish imports `.../elvish/lib/*.elv`. checklist: - no confirmation on close where the cursor is at prompt: works, only occasionally doesn't, I'm not yet sure when. - new terminals start in pwd of previously focused terminal: works - prompts resize correctly: works - triple-click while holding `ctrl` selects output of a command: works (when mouse is over the output) - cursor at the prompt is turned into a bar: works - ghostty:`jump_to_prompt` scrolls through prompts: works - `opt`-click moves cursor at the prompt: works - `sudo` preserves ghostty terminfo: untested - not sure when this is needed exactly, but did not encounter any errors after sudo, either --- .../elvish/lib/ghostty-integration.elv | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 src/shell-integration/elvish/lib/ghostty-integration.elv diff --git a/src/shell-integration/elvish/lib/ghostty-integration.elv b/src/shell-integration/elvish/lib/ghostty-integration.elv new file mode 100644 index 000000000..91bdd53d0 --- /dev/null +++ b/src/shell-integration/elvish/lib/ghostty-integration.elv @@ -0,0 +1,87 @@ +{ + 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 { + printf "\e]7;file://%s%s\a" (hostname) (pwd) + } + + fn sudo-with-terminfo {|@args| + var sudoedit = $false + put $args | each {|arg| + 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 $sudoedit { set args = [ TERMINFO=$E:TERMINFO $@args ] } + command sudo $@args + } + + 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~) + set after-chdir = (conj $after-chdir {|_| report-pwd }) + + var no-cursor = (eq 1 $E:GHOSTTY_SHELL_INTEGRATION_NO_CURSOR) + var no-sudo = (eq 1 $E:GHOSTTY_SHELL_INTEGRATION_NO_SUDO) + + if $no-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 }) + } + + try { + if (not $no-sudo) { return } + if (eq "" $E:TERMINFO) { return } + if (not-eq file (type -t sudo)) { return } + + # overwrite root namespace `sudo` + edit:add-var sudo~ $sudo-with-terminfo~ + } catch e { nop } # catch early returns, do nothing + + mark-prompt-start + report-pwd +} + From a1a97913d3698b7ff5479b28f263da2c6d462754 Mon Sep 17 00:00:00 2001 From: ilk Date: Fri, 17 May 2024 18:11:00 +0300 Subject: [PATCH 2/3] refactor(shell-integration:elvish): refactor to improve readability --- .../elvish/lib/ghostty-integration.elv | 61 ++++++++++++++----- 1 file changed, 47 insertions(+), 14 deletions(-) diff --git a/src/shell-integration/elvish/lib/ghostty-integration.elv b/src/shell-integration/elvish/lib/ghostty-integration.elv index 91bdd53d0..6af878ba5 100644 --- a/src/shell-integration/elvish/lib/ghostty-integration.elv +++ b/src/shell-integration/elvish/lib/ghostty-integration.elv @@ -1,4 +1,41 @@ { + fn restore-xdg-dirs { + var integration-dir = $E:GHOSTTY_FISH_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_FISH_XDG_DIR + } + if (and (has-env GHOSTTY_FISH_XDG_DIR) (has-env XDG_DATA_DIRS)) { + restore-xdg-dirs + } +} + +{ + # helper used by `mark-*` functions fn set-prompt-state {|new| set-env __ghostty_prompt_state $new } fn mark-prompt-start { @@ -57,31 +94,27 @@ command sudo $@args } + defer { + mark-prompt-start + report-pwd + } + + var no-cursor = (eq 1 $E:GHOSTTY_SHELL_INTEGRATION_NO_CURSOR) + var no-sudo = (eq 1 $E:GHOSTTY_SHELL_INTEGRATION_NO_SUDO) + 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~) set after-chdir = (conj $after-chdir {|_| report-pwd }) - var no-cursor = (eq 1 $E:GHOSTTY_SHELL_INTEGRATION_NO_CURSOR) - var no-sudo = (eq 1 $E:GHOSTTY_SHELL_INTEGRATION_NO_SUDO) - if $no-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 }) } - - try { - if (not $no-sudo) { return } - if (eq "" $E:TERMINFO) { return } - if (not-eq file (type -t sudo)) { return } - - # overwrite root namespace `sudo` + if (and $no-sudo (not-eq ""$E:TERMINFO) (eq file (type -t sudo))) { edit:add-var sudo~ $sudo-with-terminfo~ - } catch e { nop } # catch early returns, do nothing - - mark-prompt-start - report-pwd + } } From 17e7ff1de33a28bab9ee66a597d08783e7b35d95 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 27 May 2024 16:11:08 -0700 Subject: [PATCH 3/3] shell-integration: add README about Elvish --- src/shell-integration/README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/shell-integration/README.md b/src/shell-integration/README.md index 8aa9e8a6f..c27b45891 100644 --- a/src/shell-integration/README.md +++ b/src/shell-integration/README.md @@ -20,6 +20,14 @@ disabling POSIX mode). Bash shell integration can also be sourced manually from `bash/ghostty.bash`. +### Elvish + +The [Elvish](https://elv.sh) shell integration is supported by +the community and is not officially supported by Ghostty. We distribute +it for ease of access and use but do not provide support for it. +If you experience issues with the Elvish shell integration, I welcome +any contributions to fix them. Thank you! + ### Fish For [Fish](https://fishshell.com/), Ghostty prepends to the