eat-emacs/integration/bash
Akib Azmain Turja 9caa496e45
Tighter shell integration
* eat.el (eat--t-term): New slots: 'prompt-start-fn',
'prompt-end-fn', 'cont-prompt-start-fn', 'cont-prompt-end-fn',
'set-cmd-fn', 'cmd-start-fn', 'cmd-finish-fn'
* eat.el (eat--t-set-cwd): Accept three arguments in two
different formats.
* eat.el (eat--t-prompt-start, eat--t-prompt-end)
(eat--t-cont-prompt-start, eat--t-cont-prompt-end)
(eat--t-set-cmd, eat--t-cmd-start, eat--t-cmd-finish): New
function.
* eat.el (eat--t-handle-output): Accept Eat's own
OSC 51 ; e ; ... ST sequences.
* eat.el (eat-term-prompt-start-function)
(eat-term-prompt-end-function)
(eat-term-continuation-prompt-start-function)
(eat-term-continuation-prompt-end-function)
(eat-term-set-cmd-function, eat-term-cmd-start-function)
(eat-term-cmd-finish-function): New generalized variable.
* integration/bash (__eat_current_command, __eat_exit_status):
New variable.
* integration/bash (__eat_prompt_command): Send exit status of
last command, if applicable.  Use Eat specific sequence to
report working directory.  Set title.
* integration/bash (__eat_preexec): Report current command and
execution start.
* integration/bash (__eat_before_prompt_command): Set
'__eat_exit_status' to the exit status of the last command.
* integration/bash (__eat_prompt_start, __eat_prompt_end)
(__eat_continuation_start, __eat_continuation_end): New
variable, used as constant only to make the code more readable.
* integration/bash (__eat_enable_integration): Wrap 'PS1' and
'PS2'.  Don't set title from 'PS1'.
2022-12-06 00:21:48 +06:00

115 lines
3.6 KiB
Text

# integration/bash --- Bash integration
# Copyright (C) 2022 Akib Azmain Turja.
# This file is not part of GNU Emacs.
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3, or (at your option)
# any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# For a full copy of the GNU General Public License
# see <https://www.gnu.org/licenses/>.
__eat_current_command=""
__eat_exit_status=0
__eat_prompt_command () {
# Send exit status.
if test -n "$__eat_current_command"
then
printf '\e]51;e;H;%i\e\\' "$__eat_exit_status"
fi
__eat_current_command=""
# Send the current working directory, for directory tracking.
printf '\e]51;e;A;%s;%s\e\\' "$(printf "%s" "$HOSTNAME" | base64)" \
"$(printf "%s" "$PWD" | base64)"
# Update title.
# "${PWD/$HOME/'~'}" converts "/home/akib/org/" to "~/org/".
# The next one is substituted with '$', or '#' if we're "root".
printf '\e]2;%s@%s:%s%s\e\\' "$USER" "$HOSTNAME" \
"${PWD/$HOME/'~'}" \
"$(test $UID -eq 0 && echo '#' || echo '$')"
}
__eat_preexec () {
# Get the real command typed by the user from the history.
__eat_current_command="$(history 1 | sed 's/ *[0-9]* *//')"
# Send current command.
printf '\e]51;e;F;%s\e\\' \
"$(printf "%s" "$__eat_current_command" | base64)"
# Send pre-exec sequence.
printf '\e]51;e;G\e\\'
# Update title to including the command running.
# "${PWD/$HOME/'~'}" converts "/home/akib/org/" to "~/org/".
# The next one is substituted with '$', or '#' if we're "root".
printf '\e]2;%s@%s:%s%s %s\e\\' "$USER" "$HOSTNAME" \
"${PWD/$HOME/'~'}" \
"$(test $UID -eq 0 && echo '#' || echo '$')" \
"$__eat_current_command"
}
__eat_inhibit_preexec=yes
__eat_before_prompt_command ()
{
__eat_exit_status="$?"
__eat_inhibit_preexec=yes
}
__eat_after_prompt_command ()
{
__eat_inhibit_preexec=no
}
__eat_before_exec () {
if test $__eat_inhibit_preexec = no \
&& test "$BASH_COMMAND" != __eat_before_prompt_command
then
__eat_inhibit_preexec=yes
__eat_preexec
fi
}
__eat_prompt_start='\e]51;e;B\e\\'
__eat_prompt_end='\e]51;e;C\e\\'
__eat_continuation_start='\e]51;e;D\e\\'
__eat_continuation_end='\e]51;e;E\e\\'
__eat_enable_integration ()
{
__eat_integration_enabled=yes
PS1="\[$__eat_prompt_start\]$PS1\[$__eat_prompt_end\]"
PS2="\[$__eat_continuation_start\]$PS2\[$__eat_continuation_end\]"
PROMPT_COMMAND+=(__eat_prompt_command)
trap '__eat_before_exec' DEBUG
# Wrap 'PROMPT_COMMAND' to avoid it getting trapped in 'DEBUG' trap.
# Step 1: Append to PROMPT_COMMAND.
PROMPT_COMMAND+=(__eat_after_prompt_command)
# Step 2: Prepend to PROMPT_COMMAND.
# Step 2.1: Move all elements to make the first index free.
# Fun fact: Microsoft doesn't still about know this simple trick.
# They ended up using something as silly and pityful as
# 'VAR=$PROMPT_COMMAND' to copy a Bash array in VSCode Bash
# integration script, which simply won't work ever, and then
# complain about Bash in the comments! xD
for i in $(eval "echo {${#PROMPT_COMMAND[*]}..1..-1}")
do
PROMPT_COMMAND[$i]=${PROMPT_COMMAND[$((i-1))]}
done
# Step 2.2: Assign the first element.
PROMPT_COMMAND[0]=__eat_before_prompt_command
}
# Enable.
test -z "$__eat_integration_enabled" && __eat_enable_integration
# Local Variables:
# mode: sh
# End: