Currently, `sudo_has_sudoedit_flags` variable is being erased when `for`
block ends.
Change its scope to `--function` to prevent this.
Fixes `sudo: you may not specify environment variables in edit mode`.
The intention of #5075 was to create a less intrusive, more hermetic
environment in which to source the bash startup files. This caused
problems for multiple people, and I believe that's because the general
expectation is that these files are sourced at global (not function)
scope.
For example, when a file is sourced from within a function scope, any
variables that weren't explicitly exported into the global environment
won't be available outside of the scope of the function. Most system and
personal startup files aren't written with that constraint because it's
not how bash itself loads these files.
As a small improvement over the original code, `rcfile` has been renamed
to `__ghostty_rcfile`. Avoiding leaking this variable while sourcing
these files was a goal of #5075, and prefixing it make it much less of a
potential issue.
This change also reverts the $HOME to ~/ change. While the ~/ notation
is more concise, using $HOME is more common and easier to implement
safely with regard to quoting.
We use `trap` to bootstrap our installation function (__bp_install). We
remove our code upon first execution but need to restore any preexisting
trap calls. We previously used `sed` to process the trap string, but
that had two downsides:
1. `sed` is an external command dependency. It needs to exist on the
system, and we need to invoke it in a subshell (which has some runtime
cost).
2. The regular expression pattern was imperfect and didn't handle
trickier cases like `'` characters in the trap string:
$ (trap "echo 'hello'" DEBUG; trap -p DEBUG)
hello
trap -- 'echo '\''hello'\''' DEBUG
This change removes the dependency on `sed` by locally evaluating the
trap string and extracting any prior trap. This works reliably because
we control the format our trap string, which looks like this (with
newlines expanded):
__bp_trap_string="$(trap -p DEBUG)"
trap - DEBUG
__bp_install
Upstream: https://github.com/rcaloras/bash-preexec/pull/170
We post-process history 1's output to extract the current command. This
processing needs to strip the leading history number, an optional *
character indicating whether the entry was modified (or a space), and
then a space separating character.
We were previously using sed(1) for this, but we can implement an
equivalent transformation using bash's native parameter expansion
syntax.
This also results in ~4x reduction in per-prompt command overhead.
Upstream: https://github.com/rcaloras/bash-preexec/pull/167
We use `trap` to bootstrap our installation function (__bp_install). We
remove our code upon first execution but need to restore any preexisting
trap calls. We previously used `sed` to process the trap string, but
that had two downsides:
1. `sed` is an external command dependency. It needs to exist on the
system, and we need to invoke it in a subshell (which has some
runtime cost).
2. The regular expression pattern was imperfect and didn't handle
trickier cases like `'` characters in the trap string:
$ (trap "echo 'hello'" DEBUG; trap -p DEBUG)
hello
trap -- 'echo '\''hello'\''' DEBUG
This change removes the dependency on `sed` by locally evaluating the
trap string and extracting any prior trap. This works reliably because
we control the format our trap string, which looks like this (with
newlines expanded):
__bp_trap_string="$(trap -p DEBUG)"
trap - DEBUG
__bp_install
We post-process history 1's output to extract the current command. This
processing needs to strip the leading history number, an optional *
character indicating whether the entry was modified (or a space), and
then a space separating character.
We were previously using sed(1) for this, but we can implement an
equivalent transformation using bash's native parameter expansion
syntax.
This also results in ~4x reduction in per-prompt command overhead.
We now use a temporary function (__ghostty_bash_startup) to perform the
bash startup sequence. This gives us a local function scope in which to
store some temporary values (like rcfile). This way, they won't leak
into the sourced files' scopes.
Also, use `~/` instead of `$HOME` for home directory paths as a simpler
shorthand notation.
'--posix' starts bash in POSIX mode (like /bin/sh). This is rarely used
for interactive shells, and removing automatic shell integration support
for this option allows us to simply/remove some exceptional code paths.
Users are still able to manually source the shell integration script.
Also fix an issue where we would still inject GHOSTTY_BASH_RCFILE if we
aborted the automatic shell integration path _after_ seeing an --rcfile
or --init-file argument.
PS0 is evaluated after a command is read but before it is executed. The
'preexec' hook (from bash-preexec) is equivalent for our title-updating
purposes and conveniently provides the current command as an argument
(from its own `history 1` call).
'--posix' starts bash in POSIX mode (like /bin/sh). This is rarely used
for interactive shells, and removing automatic shell integration support
for this option allows us to simply/remove some exceptional code paths.
Users are still able to manually source the shell integration script.
Also fix an issue where we would still inject GHOSTTY_BASH_RCFILE if we
aborted the automatic shell integration path _after_ seeing an --rcfile
or --init-file argument.
PS0 is evaluated after a command is read but before it is executed. The
'preexec' hook (from bash-preexec) is equivalent for our title-updating
purposes and conveniently provides the current command as an argument
(from its own `history 1` call).
The previous approach to wrapping `sudo` had a few shortcomings:
1. We were (re)defining our 'sudo' function wrapper in the "precmd"
path. It only needs to be defined once in the shell session.
2. If there was an existing 'sudo' alias, the function definition would
conflict and result in a syntax error.
Fix (1) by hoisting the 'sudo' function into global scope. I also
considered only defining our wrapper if an executable `sudo` binary
could be found (e.g. `-x $(builtin command -v sudo)`, but let's keep the
existing behavior for now. This allows for a `sudo` command to be
installed later in the shell session and still be wrapped.
Address (2) by defining the wrapper function using `function sudo`
(instead of `sudo()`) syntax. An explicit function definition won't
clash with an existing 'sudo' alias, although the alias will continue to
take precedence (i.e. our wrapper won't be called). If the alias is
defined _after_ our 'sudo' function is defined, our function will call
the aliased command.
This ordering is relevant because it can result in different behaviors
depending on when a user defines their aliases relative to sourcing the
shell integration script. Our recommendation remains that users either
use automatic shell injection or manually source the shell integration
script _before_ other things in their `.bashrc`, so that aligns with the
expected behavior of the 'sudo' wrapper with regard to aliases. Given
that, I don't think we need any more explicit user-facing documentation
on this beyond the script-level comments.
The previous approach to wrapping `sudo` had a few shortcomings:
1. We were (re)defining our 'sudo' function wrapper in the "precmd"
path. It only needs to be defined once in the shell session.
2. If there was an existing 'sudo' alias, the function definition would
conflict and result in a syntax error.
Fix (1) by hoisting the 'sudo' function into global scope. I also
considered only defining our wrapper if an executable `sudo` binary
could be found (e.g. `-x $(builtin command -v sudo)`, but let's keep the
existing behavior for now. This allows for a `sudo` command to be
installed later in the shell session and still be wrapped.
Address (2) by defining the wrapper function using `function sudo`
(instead of `sudo()`) syntax. An explicit function definition won't
clash with an existing 'sudo' alias, although the alias will continue to
take precedence (i.e. our wrapper won't be called). If the alias is
defined _after_ our 'sudo' function is defined, our function will call
the aliased command.
This ordering is relevant because it can result in different behaviors
depending on when a user defines their aliases relative to sourcing the
shell integration script. Our recommendation remains that users either
use automatic shell injection or manually source the shell integration
script _before_ other things in their `.bashrc`, so that aligns with the
expected behavior of the 'sudo' wrapper with regard to aliases. Given
that, I don't think we need any more explicit user-facing documentation
on this beyond the script-level comments.
`type` is a bash builtin and should not be used in elvish.
```
Exception: exec: "type": executable file not found in $PATH
ghostty-integration.elv:120:60-71: if (and (not $no-sudo) (not-eq "" $E:TERMINFO) (eq file (type -t sudo))) {
ghostty-integration.elv:38:1-123:1:
```
We can use the elvish `has-external` function instead.
We used a mix of shorthand and octal representations when printing these
control characters. Standardize on the shorter, more readable shorthand
notation because that's what we use in the other shell integration
scripts.
Bash doesn't redraw the leading lines in a multiline prompt so we mark
the last line as a secondary prompt (k=s) to prevent the preceding lines
from being erased by Ghostty after a resize.
Our previously attempt at this (#1973) was flawed. Instead, we now just
re-issue the OSC "133;A" command with a 'k=s' (secondary) kind at the
end of our prompt string.
This isn't a great solution because it stomps on the prompt's "133;B"
command (end of prompt and start of user input), but it's sufficient for
now and only applies in the multiline prompt case.
Going forward, we should revisit our semantic prompt implementation. Our
row-based approach is too limiting; lines can have multiple markers, and
markers should be recorded with their full coordinates so they can form
ranges.
See: https://per.bothner.com/blog/2019/shell-integration-proposal/
Bash shell detection was originally disabled in #1823 due to problems
with /bin/bash on macOS.
Apple distributes their own patched version of Bash 3.2 on macOS that
disables the POSIX-style $ENV-based startup path:
e5397a7e74/bash-3.2/shell.c (L1112-L1114)
This means we're unable to perform our automatic shell integration
sequence in this specific environment. Standard Bash 3.2 works fine.
Knowing this, we can re-enable bash shell detection by default unless
we're running "/bin/bash" on Darwin. We can safely assume that's the
unsupported Bash executable because /bin is non-writable on modern macOS
installations due to System Integrity Protection.
macOS users can either manually source our shell integration script
(which otherwise works fine with Apple's Bash) or install a standard
version of Bash from Homebrew or elsewhere.
Fails on NixOS
/nix/store/lkqjw3yazk7d3il8a0rp11pvjxdyq281-ghostty-0.1.0/share/ghostty/shell-integration/zsh/ghostty-integration: bad interpreter: /bin/zsh: no such file or directory
These OSC 133 semantic prompt sequences mark the primary and secondary
parts of the prompt strings.
PS1 is marked as the primary (initial) prompt. This is the default, so
we could skip emitting these sequences, but they're added here for now
to be explicit and consistent with what other terminal emulators do in
their shell integrations.
If PS1 is a multiline prompt (i.e. it contains a newline), we mark the
last line as a secondary prompt (k=s). bash doesn't redraw the leading
lines of a multiline prompt on its own, so we can use this information
at runtime to prevent the preceding lines from being erased by ghostty
after a resize.
PS2 is always marked as a secondary prompt.
For now, bash integration must be explicitly enabled (by setting
`shell-integration = bash`). Our automatic shell integration requires
bash version 4 or later, and systems like macOS continue to ship bash
version 3 by default. This approach avoids the cost of performing a
runtime version check.
Fish automatic integration taken as an example.
Just like fish, Elvish checks `XDG_DATA_DIRS` for its modules.
Thus, Fish integration in zig is reused, and integration in
Elvish now removes `GHOSTTY_FISH_XDG_DIR` environment variable
on launch.
- 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