Adding preexec as a vendored library

Added a vendored lib loading routine in bash-it.sh
Added documentation on how to vendor libs in bash-it
Added and fixed plugins using preexec
Added tests for two plugins
Removed the old preexec lib
pull/1776/head
buhl 2021-01-08 23:51:37 +01:00
parent 5ad497924c
commit 29855ed1e6
12 changed files with 252 additions and 260 deletions

View File

@ -49,6 +49,17 @@ do
fi
done
# Load vendors
BASH_IT_LOG_PREFIX="vendor: "
for _bash_it_vendor_init in "${BASH_IT}"/vendor/init.d/*.bash
do
_log_debug "Loading \"$(basename "${_bash_it_vendor_init}" .bash)\"..."
# shellcheck disable=SC1090
source "${_bash_it_vendor_init}"
done
unset _bash_it_vendor_init
BASH_IT_LOG_PREFIX="core: main: "
# Load the global "enabled" directory
# "family" param is empty so that files get sources in glob order
# shellcheck source=./scripts/reloader.bash
@ -62,7 +73,7 @@ do
done
# Load theme, if a theme was set
if [[ ! -z "${BASH_IT_THEME}" ]]; then
if [[ -n "${BASH_IT_THEME}" ]]; then
_log_debug "Loading \"${BASH_IT_THEME}\" theme..."
# Load colors and helpers first so they can be used in base theme
BASH_IT_LOG_PREFIX="themes: colors: "

View File

@ -39,10 +39,13 @@ themes/barbuk
themes/atomic
themes/axin
themes/base.theme.bash
themes/command_duration.theme.bash
# plugins
#
plugins/available/basher.plugin.bash
plugins/available/cmd-returned-notify.plugin.bash
plugins/available/xterm.plugin.bash
# completions
#

View File

@ -38,6 +38,7 @@ The main ``bash_it.sh`` script loads the frameworks individual components in the
* ``lib/composure.bash``
* ``vendor/init.d/*.bash``
* Files in ``lib`` with the exception of ``appearance.bash`` - this means that ``composure.bash`` is loaded again here (possible improvement?)
* Enabled ``aliases``
* Enabled ``plugins``
@ -78,6 +79,65 @@ Having the order based on a numeric priority in a common directory allows for mo
These items are subject to change. When making changes to the internal functionality, this page needs to be updated as well.
Working with vendored libs
--------------------------
Vendored libs are external libraries, meaning source code not maintained by Bash-it
developers.
They are ``git subtrees`` curated in the ``vendor/`` folder. To ease the work with git
vendored libs as subtrees we use the `git-vendor <https://github.com/Tyrben/git-vendor>`_ tool.
The `original repo <https://github.com/brettlangdon/git-vendor>`_ for git vendor is
unmaintained so for now we are recommending Tyrben's fork.
For more information on ``git vendor`` there are a short `usage description <https://github.com/Tyrben/git-vendor#usage>`_
in the repositories ``README`` file and a website for the original repository has a `manual page <https://brettlangdon.github.io/git-vendor/>`_ which is also included in both
repositories.
To support a flexible loading of external libraries, a file unique to the vendored
library must be placed in ``vendor/init.d/`` with the ``.bash`` extension.
Rebasing a feature branch with an added/updated vendored library
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
If your feature branch with a newly added/updated vendored lib has fallen behind master
you might need to rebase it before creating a PR. However rebasing with dangling
subtree commits can cause problems.
The following rebase strategy will pause the rebase at the point where you added a
subtree and let you add it again before continuing the rebasing.
::
[feature/branch] $ git rebase --rebase-merges --strategy subtree master
fatal: refusing to merge unrelated histories
Could not apply 0d6a56b... Add-preexec-from-https-github-com-rcaloras-bash-preexec-0-4-1- # Add "preexec" from "https://github.com/rcaloras/bash-preexec@0.4.1"
[feature/branch] $ git vendor add preexec https://github.com/rcaloras/bash-preexec 0.4.1
...
[feature/branch] $ git rebase --continue
If rebasing makes you a little uneasy (as it probably should). You can always test in
another branch.
::
[feater/branch] $ git checkout -b feature/branch-test-rebase
[feater/branch-test-rebase] $ git rebase --rebase-merges --strategy subtree master
...
Afterwards you can make sure the rebase was successful by running ``git vendor list``
to see if your library is still recognized as a vendored lib
::
[feature/branch] $ git vendor list
preexec@0.4.1:
name: preexec
dir: vendor/github.com/rcaloras/bash-preexec
repo: https://github.com/rcaloras/bash-preexec
ref: 0.4.1
commit: 8fe585c5cf377a3830b895fe26e694b020d8db1a
[feature/branch] $
Plugin Disable Callbacks
------------------------

View File

@ -1,199 +0,0 @@
#!/usr/bin/env bash
# http://www.twistedmatrix.com/users/glyph/preexec.bash.txt
# preexec.bash -- Bash support for ZSH-like 'preexec' and 'precmd' functions.
# The 'preexec' function is executed before each interactive command is
# executed, with the interactive command as its argument. The 'precmd'
# function is executed before each prompt is displayed.
# To use, in order:
# 1. source this file
# 2. define 'preexec' and/or 'precmd' functions (AFTER sourcing this file),
# 3. as near as possible to the end of your shell setup, run 'preexec_install'
# to kick everything off.
# Note: this module requires 2 bash features which you must not otherwise be
# using: the "DEBUG" trap, and the "PROMPT_COMMAND" variable. preexec_install
# will override these and if you override one or the other this _will_ break.
# This is known to support bash3, as well as *mostly* support bash2.05b. It
# has been tested with the default shells on MacOS X 10.4 "Tiger", Ubuntu 5.10
# "Breezy Badger", Ubuntu 6.06 "Dapper Drake", and Ubuntu 6.10 "Edgy Eft".
# Copy screen-run variables from the remote host, if they're available.
if [[ "$SCREEN_RUN_HOST" == "" ]]
then
SCREEN_RUN_HOST="$LC_SCREEN_RUN_HOST"
SCREEN_RUN_USER="$LC_SCREEN_RUN_USER"
fi
# This variable describes whether we are currently in "interactive mode";
# i.e. whether this shell has just executed a prompt and is waiting for user
# input. It documents whether the current command invoked by the trace hook is
# run interactively by the user; it's set immediately after the prompt hook,
# and unset as soon as the trace hook is run.
preexec_interactive_mode=""
# Default do-nothing implementation of preexec.
function preexec () {
true
}
# Default do-nothing implementation of precmd.
function precmd () {
true
}
# This function is installed as the PROMPT_COMMAND; it is invoked before each
# interactive prompt display. It sets a variable to indicate that the prompt
# was just displayed, to allow the DEBUG trap, below, to know that the next
# command is likely interactive.
function preexec_invoke_cmd () {
precmd
preexec_interactive_mode="yes"
}
# This function is installed as the DEBUG trap. It is invoked before each
# interactive prompt display. Its purpose is to inspect the current
# environment to attempt to detect if the current command is being invoked
# interactively, and invoke 'preexec' if so.
function preexec_invoke_exec () {
if [[ -n "$COMP_LINE" ]]
then
# We're in the middle of a completer. This obviously can't be
# an interactively issued command.
return
fi
if [[ -z "$preexec_interactive_mode" ]]
then
# We're doing something related to displaying the prompt. Let the
# prompt set the title instead of me.
return
else
# If we're in a subshell, then the prompt won't be re-displayed to put
# us back into interactive mode, so let's not set the variable back.
# In other words, if you have a subshell like
# (sleep 1; sleep 2)
# You want to see the 'sleep 2' as a set_command_title as well.
if [[ 0 -eq "$BASH_SUBSHELL" ]]
then
preexec_interactive_mode=""
fi
fi
if [[ "preexec_invoke_cmd" == "$BASH_COMMAND" ]]
then
# Sadly, there's no cleaner way to detect two prompts being displayed
# one after another. This makes it important that PROMPT_COMMAND
# remain set _exactly_ as below in preexec_install. Let's switch back
# out of interactive mode and not trace any of the commands run in
# precmd.
# Given their buggy interaction between BASH_COMMAND and debug traps,
# versions of bash prior to 3.1 can't detect this at all.
preexec_interactive_mode=""
return
fi
# In more recent versions of bash, this could be set via the "BASH_COMMAND"
# variable, but using history here is better in some ways: for example, "ps
# auxf | less" will show up with both sides of the pipe if we use history,
# but only as "ps auxf" if not.
local this_command=`history 1 | sed -e "s/^[ ]*[0-9]*[ ]*//g"`;
# If none of the previous checks have earlied out of this function, then
# the command is in fact interactive and we should invoke the user's
# preexec hook with the running command as an argument.
preexec "$this_command"
}
# Execute this to set up preexec and precmd execution.
function preexec_install () {
# *BOTH* of these options need to be set for the DEBUG trap to be invoked
# in ( ) subshells. This smells like a bug in bash to me. The null stderr
# redirections are to quiet errors on bash2.05 (i.e. OSX's default shell)
# where the options can't be set, and it's impossible to inherit the trap
# into subshells.
set -o functrace > /dev/null 2>&1
shopt -s extdebug > /dev/null 2>&1
# Finally, install the actual traps.
if [[ ! -z "${PROMPT_COMMAND// }" ]]; then
PROMPT_COMMAND="${PROMPT_COMMAND}"$'\n'"preexec_invoke_cmd"
else
PROMPT_COMMAND="preexec_invoke_cmd"
fi
trap 'preexec_invoke_exec' DEBUG
}
# Since this is the reason that 99% of everybody is going to bother with a
# pre-exec hook anyway, we'll include it in this module.
# Change the title of the xterm.
function preexec_xterm_title () {
local title="$1"
echo -ne "\033]0;$title\007" > /dev/stderr
}
function preexec_screen_title () {
local title="$1"
echo -ne "\033k$1\033\\" > /dev/stderr
}
# Abbreviate the "user@host" string as much as possible to preserve space in
# screen titles. Elide the host if the host is the same, elide the user if the
# user is the same.
function preexec_screen_user_at_host () {
local RESULT=""
if [[ "$SCREEN_RUN_HOST" == "$SCREEN_HOST" ]]
then
return
else
if [[ "$SCREEN_RUN_USER" == "$USER" ]]
then
echo -n "@${SCREEN_HOST}"
else
echo -n "${USER}@${SCREEN_HOST}"
fi
fi
}
function preexec_xterm_title_install () {
# These functions are defined here because they only make sense with the
# preexec_install below.
function precmd () {
preexec_xterm_title "${TERM} - ${USER}@${SCREEN_HOST} `dirs -0` $PROMPTCHAR"
if [[ "${TERM}" == screen ]]
then
preexec_screen_title "`preexec_screen_user_at_host`${PROMPTCHAR}"
fi
}
function preexec () {
preexec_xterm_title "${TERM} - $1 {`dirs -0`} (${USER}@${SCREEN_HOST})"
if [[ "${TERM}" == screen ]]
then
local cutit="$1"
local cmdtitle=`echo "$cutit" | cut -d " " -f 1`
if [[ "$cmdtitle" == "exec" ]]
then
local cmdtitle=`echo "$cutit" | cut -d " " -f 2`
fi
if [[ "$cmdtitle" == "screen" ]]
then
# Since stacked screens are quite common, it would be nice to
# just display them as '$$'.
local cmdtitle="${PROMPTCHAR}"
else
local cmdtitle=":$cmdtitle"
fi
preexec_screen_title "`preexec_screen_user_at_host`${PROMPTCHAR}$cmdtitle"
fi
}
preexec_install
}

View File

@ -0,0 +1,16 @@
# shellcheck shell=bash
cite about-plugin
about-plugin 'Alert (BEL) when process ends after a threshold of seconds'
precmd_return_notification() {
export LAST_COMMAND_DURATION=$(($(date +%s) - ${LAST_COMMAND_TIME:=$(date +%s)}))
[[ ${LAST_COMMAND_DURATION} -gt ${NOTIFY_IF_COMMAND_RETURNS_AFTER:-5} ]] && echo -e "\a"
export LAST_COMMAND_TIME=
}
preexec_return_notification() {
[ -z "${LAST_COMMAND_TIME}" ] && export LAST_COMMAND_TIME=$(date +%s)
}
precmd_functions+=(precmd_return_notification)
preexec_functions+=(preexec_return_notification)

View File

@ -1,30 +1,33 @@
# shellcheck shell=bash
cite about-plugin
about-plugin 'automatically set your xterm title with host and location info'
_short-dirname () {
local dir_name=`dirs +0`
[ "$SHORT_TERM_LINE" = true ] && [ ${#dir_name} -gt 8 ] && echo ${dir_name##*/} || echo $dir_name
_short-dirname() {
local dir_name=$(dirs +0)
[ "$SHORT_TERM_LINE" = true ] && [ "${#dir_name}" -gt 8 ] && echo "${dir_name##*/}" || echo "${dir_name}"
}
_short-command () {
local input_command="$@"
[ "$SHORT_TERM_LINE" = true ] && [ ${#input_command} -gt 8 ] && echo ${input_command%% *} || echo $input_command
_short-command() {
local input_command="$*"
[ "$SHORT_TERM_LINE" = true ] && [ "${#input_command}" -gt 8 ] && echo "${input_command%% *}" || echo "${input_command}"
}
set_xterm_title () {
set_xterm_title() {
local title="$1"
echo -ne "\033]0;$title\007"
}
precmd () {
set_xterm_title "${SHORT_USER:-${USER}}@${SHORT_HOSTNAME:-${HOSTNAME}} `_short-dirname` $PROMPTCHAR"
precmd_xterm_title() {
set_xterm_title "${SHORT_USER:-${USER}}@${SHORT_HOSTNAME:-${HOSTNAME}} $(_short-dirname) $PROMPT_CHAR"
}
preexec () {
set_xterm_title "`_short-command $1` {`_short-dirname`} (${SHORT_USER:-${USER}}@${SHORT_HOSTNAME:-${HOSTNAME}})"
preexec_xterm_title() {
set_xterm_title "$(_short-command "${1}") {$(_short-dirname)} (${SHORT_USER:-${USER}}@${SHORT_HOSTNAME:-${HOSTNAME}})"
}
case "$TERM" in
xterm*|rxvt*) preexec_install;;
xterm* | rxvt*)
precmd_functions+=(precmd_xterm_title)
preexec_functions+=(preexec_xterm_title)
;;
esac

View File

View File

View File

@ -0,0 +1,47 @@
#!/usr/bin/env bats
load ../test_helper
load ../../lib/helpers
load ../../lib/composure
load ../../plugins/available/cmd-returned-notify.plugin
@test "plugins cmd-returned-notify: notify after elapsed time" {
export NOTIFY_IF_COMMAND_RETURNS_AFTER=0
export LAST_COMMAND_TIME=$(date +%s)
sleep 1
run precmd_return_notification
assert_success
assert_output $'\a'
}
@test "plugins cmd-returned-notify: do not notify before elapsed time" {
export NOTIFY_IF_COMMAND_RETURNS_AFTER=10
export LAST_COMMAND_TIME=$(date +%s)
sleep 1
run precmd_return_notification
assert_success
assert_output $''
}
@test "plugins cmd-returned-notify: preexec no output" {
export LAST_COMMAND_TIME=
run preexec_return_notification
assert_success
assert_output ""
}
@test "plugins cmd-returned-notify: preexec no output env set" {
export LAST_COMMAND_TIME=$(date +%s)
run preexec_return_notification
assert_failure
assert_output ""
}
@test "plugins cmd-returned-notify: preexec set LAST_COMMAND_TIME" {
export LAST_COMMAND_TIME=
assert_equal "${LAST_COMMAND_TIME}" ""
NOW=$(date +%s)
preexec_return_notification
assert_equal "${LAST_COMMAND_TIME}" "${NOW}"
}

View File

@ -0,0 +1,54 @@
#!/usr/bin/env bats
load ../test_helper
load ../../lib/composure
load ../../plugins/available/xterm.plugin
function local_setup {
setup_test_fixture
# Copy the test fixture to the Bash-it folder
if command_exists -v rsync
then
rsync -a "$BASH_IT/test/fixtures/plugin/xterm/" "$BASH_IT/"
else
find "$BASH_IT/test/fixtures/plugin/xterm" \
-mindepth 1 -maxdepth 1 \
-exec cp -r {} "$BASH_IT/" \;
fi
}
@test "plugins xterm: shorten command output" {
export SHORT_TERM_LINE=true
run _short-command ${BASH_IT}/test/fixtures/plugin/xterm/files/*
assert_success
assert_output ${BASH_IT}/test/fixtures/plugin/xterm/files/arg0
}
@test "plugins xterm: full command output" {
export SHORT_TERM_LINE=false
run _short-command ${BASH_IT}/test/fixtures/plugin/xterm/files/*
assert_success
assert_output "$(echo ${BASH_IT}/test/fixtures/plugin/xterm/files/*)"
}
@test "plugins xterm: shorten dirname output" {
export SHORT_TERM_LINE=true
run _short-dirname
assert_success
assert_output "$(basename $PWD)"
}
@test "plugins xterm: full dirname output" {
export SHORT_TERM_LINE=false
run _short-dirname
assert_success
assert_output $PWD
}
@test "plugins xterm: set xterm title" {
run set_xterm_title title
assert_success
assert_output $'\033]0;title\007'
}

View File

@ -1,4 +1,4 @@
#!/usr/bin/env bash
# shellcheck shell=bash
if [ -z "$BASH_IT_COMMAND_DURATION" ] || [ "$BASH_IT_COMMAND_DURATION" != true ]; then
_command_duration() {
@ -41,33 +41,29 @@ _command_duration() {
current_time_deciseconds=$((10#${current_time#*.}))
# seconds
command_duration=$(( current_time_seconds - command_start_sseconds ))
command_duration=$((current_time_seconds - command_start_sseconds))
if (( current_time_deciseconds >= command_start_deciseconds )); then
deciseconds=$(( (current_time_deciseconds - command_start_deciseconds) ))
if ((current_time_deciseconds >= command_start_deciseconds)); then
deciseconds=$(((current_time_deciseconds - command_start_deciseconds)))
else
((command_duration-=1))
deciseconds=$(( 10 - ( (command_start_deciseconds - current_time_deciseconds) ) ))
((command_duration -= 1))
deciseconds=$((10 - ((command_start_deciseconds - current_time_deciseconds))))
fi
command rm "$COMMAND_DURATION_FILE"
else
command_duration=0
fi
if (( command_duration > 0 )); then
minutes=$(( command_duration / 60 ))
seconds=$(( command_duration % 60 ))
if ((command_duration > 0)); then
minutes=$((command_duration / 60))
seconds=$((command_duration % 60))
fi
if (( minutes > 0 )); then
if ((minutes > 0)); then
printf "%s%s%dm %ds" "$COMMAND_DURATION_ICON" "$COMMAND_DURATION_COLOR" "$minutes" "$seconds"
elif (( seconds >= COMMAND_DURATION_MIN_SECONDS )); then
elif ((seconds >= COMMAND_DURATION_MIN_SECONDS)); then
printf "%s%s%d.%01ds" "$COMMAND_DURATION_ICON" "$COMMAND_DURATION_COLOR" "$seconds" "$deciseconds"
fi
}
preexec() (
_command_duration_pre_exec
)
preexec_install
preexec_functions+=(_command_duration_pre_exec)

1
vendor/init.d/preexec.bash vendored 100644
View File

@ -0,0 +1 @@
source ${BASH_IT}/vendor/github.com/rcaloras/bash-preexec/bash-preexec.sh