mirror of
https://github.com/lloeki/dotfiles.git
synced 2025-12-06 07:24:39 +01:00
181 lines
5.4 KiB
Bash
181 lines
5.4 KiB
Bash
# Make Bash more like Zsh
|
|
|
|
|
|
## Hooks
|
|
# https://zsh.sourceforge.io/Doc/Release/Functions.html#Hook-Functions
|
|
|
|
# TODO: support precmd_functions and chpwd_functions arrays
|
|
|
|
### Defaults: NOOPs
|
|
|
|
function preexec { :; }
|
|
|
|
function precmd { :; }
|
|
|
|
function chpwd { :; }
|
|
|
|
### Implementations
|
|
|
|
# Alternative cd function that calls chpwd afterwards builtin cd
|
|
#
|
|
# zsh: Executed whenever the current working directory is changed.
|
|
__cd_invoke_chpwd() {
|
|
builtin cd "$@"
|
|
|
|
chpwd
|
|
|
|
# TODO: chpwd_functions array
|
|
}
|
|
|
|
# zsh: Executed before each prompt. Note that precommand functions are not
|
|
# re-executed simply because the command line is redrawn, as happens, for
|
|
# example, when a notification about an exiting job is displayed.
|
|
__prompt_command_invoke_precmd() {
|
|
precmd
|
|
|
|
# TODO: precmd_functions array
|
|
}
|
|
|
|
# to properly trigger only before command exec this must be the last prompt command
|
|
__prompt_command_hook_preexec() {
|
|
trap '__debug_invoke_preexec' DEBUG
|
|
}
|
|
|
|
__prompt_command_unhook_preexec() {
|
|
# Unfortunately there's no way to remove just one trap from a signal
|
|
trap - DEBUG
|
|
}
|
|
|
|
# zsh: Executed just after a command has been read and is about to be executed. If
|
|
# the history mechanism is active (regardless of whether the line was discarded
|
|
# from the history buffer), the string that the user typed is passed as the
|
|
# first argument, otherwise it is an empty string. The actual command that will
|
|
# be executed (including expanded aliases) is passed in two different forms:
|
|
# the second argument is a single-line, size-limited version of the command
|
|
# (with things like function bodies elided); the third argument contains the
|
|
# full text that is being executed.
|
|
__debug_invoke_preexec () {
|
|
# somehow PROMPT_COMMAND contents seems to be still affected by the
|
|
# just-removed trap. Ignore ours.
|
|
[[ "$BASH_COMMAND" == "__prompt_command_unhook_preexec" ]] && return
|
|
[[ "$BASH_COMMAND" == "__prompt_command_invoke_precmd" ]] && return
|
|
[[ "$BASH_COMMAND" == "__prompt_command_hook_preexec" ]] && return
|
|
|
|
# TODO: not sure this is still necessary
|
|
[[ -n "$COMP_LINE" ]] && return # completion
|
|
|
|
local this_command;
|
|
|
|
# TODO: hack: doesn't work when command is not added to history
|
|
this_command=`history 1 | sed -e "s/^[ ]*[0-9]*[ ]*//g"`;
|
|
preexec "$this_command"
|
|
}
|
|
|
|
### Set up the hooks
|
|
|
|
# Define a function to override the cd builtin
|
|
#
|
|
# - does not apply to `bash -c 'cd foo`
|
|
# - does apply to shubshells: `(cd foo)` will call so side effects beware (env
|
|
# vars are conveniently scoped to subshell)
|
|
cd() { __cd_invoke_chpwd "$@"; }
|
|
|
|
# Call on each prompt. This transitively sets preexec to work for each prompt
|
|
#
|
|
# PROMPT_COMMAND is evaluated right before PS1 is displayed. This matches zsh
|
|
# semantics for precmd.
|
|
#
|
|
# We forcefully set it instead of taking whatever was set:
|
|
# - to control order (precmd eval + preexec hooking must be last)
|
|
# - because of the trap issue
|
|
PROMPT_COMMAND=(
|
|
# this also conveniently clears DEBUG traps
|
|
__prompt_command_unhook_preexec
|
|
|
|
# precmd hook processing
|
|
__prompt_command_invoke_precmd
|
|
|
|
# must be last to properly have DEBUG trigger only before command exec
|
|
__prompt_command_hook_preexec
|
|
)
|
|
|
|
|
|
## Other bits
|
|
|
|
# clears a line that was not terminated by a LF fixing the dangling prompt
|
|
# issue by marking it with a reversed %, like zsh
|
|
clear_incomplete_line() {
|
|
local row
|
|
local col
|
|
|
|
# ask for cursor position then read answer
|
|
# TODO: fix read: mashing keyboard makes syntax errors
|
|
stty -echo
|
|
echo -en "\033[6n"
|
|
IFS=';' read -r -d R -a pos
|
|
stty echo
|
|
|
|
# extract tput-compatible answer
|
|
row=$(( ${pos[0]:2} - 1 ))
|
|
col=$(( ${pos[1]} - 1 ))
|
|
|
|
# not on first column? do clean up: fill with spaces and rag left
|
|
if [[ $col != 0 ]]; then
|
|
printf "\e[7m%%\e[m"
|
|
printf "%*s\r" $(( COLUMNS - $col ))
|
|
fi
|
|
}
|
|
|
|
function sub_prompt_colors_unsized() {
|
|
sed \
|
|
-e 's#%F{black}#\\033[30m#g' \
|
|
-e 's#%F{red}#\\033[31m#g' \
|
|
-e 's#%F{green}#\\033[32m#g' \
|
|
-e 's#%F{yellow}#\\033[33m#g' \
|
|
-e 's#%F{blue}#\\033[34m#g' \
|
|
-e 's#%F{magenta}#\\033[35m#g' \
|
|
-e 's#%F{cyan}#\\033[36m#g' \
|
|
-e 's#%F{white}#\\033[37m#g' \
|
|
-e 's#%f#\\033[00m#g'
|
|
}
|
|
|
|
function sub_prompt_colors_sized() {
|
|
sed \
|
|
-e 's#%F{black}#\\[\\033[30m\\]#g' \
|
|
-e 's#%F{red}#\\[\\033[31m\\]#g' \
|
|
-e 's#%F{green}#\\[\\033[32m\\]#g' \
|
|
-e 's#%F{yellow}#\\[\\033[33m\\]#g' \
|
|
-e 's#%F{blue}#\\[\\033[34m\\]#g' \
|
|
-e 's#%F{magenta}#\\[\\033[35m\\]#g' \
|
|
-e 's#%F{cyan}#\\[\\033[36m\\]#g' \
|
|
-e 's#%F{white}#\\[\\033[37m\\]#g' \
|
|
-e 's#%f#\\[\\033[00m\\]#g'
|
|
}
|
|
|
|
function strip_prompt_colors() {
|
|
sed \
|
|
-e 's#%F{black}##g' \
|
|
-e 's#%F{red}##g' \
|
|
-e 's#%F{green}##g' \
|
|
-e 's#%F{yellow}##g' \
|
|
-e 's#%F{blue}##g' \
|
|
-e 's#%F{magenta}##g' \
|
|
-e 's#%F{cyan}##g' \
|
|
-e 's#%F{white}##g' \
|
|
-e 's#%f##g'
|
|
}
|
|
|
|
# right prompt support and PROMPT/RPROMPT vars
|
|
function apply_prompt_rprompt() {
|
|
local rprompt=$(echo "${RPROMPT}" | sub_prompt_colors_unsized)
|
|
local prompt=$(echo "${PROMPT}" | sub_prompt_colors_sized)
|
|
#local rprompt=$(echo "${RPROMPT}" | strip_prompt_colors)
|
|
#local prompt=$(echo "${PROMPT}" | strip_prompt_colors)
|
|
if [[ -n "${RPROMPT}" ]]; then
|
|
PS1="$(printf "\[%*s\r\]%s" "${COLUMNS}" "${rprompt:-}" "${prompt:-}")"
|
|
else
|
|
PS1="${prompt:-}"
|
|
fi
|
|
}
|
|
|
|
# vim: ft=bash
|