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 fi
done 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 # Load the global "enabled" directory
# "family" param is empty so that files get sources in glob order # "family" param is empty so that files get sources in glob order
# shellcheck source=./scripts/reloader.bash # shellcheck source=./scripts/reloader.bash
@ -62,7 +73,7 @@ do
done done
# Load theme, if a theme was set # 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..." _log_debug "Loading \"${BASH_IT_THEME}\" theme..."
# Load colors and helpers first so they can be used in base theme # Load colors and helpers first so they can be used in base theme
BASH_IT_LOG_PREFIX="themes: colors: " BASH_IT_LOG_PREFIX="themes: colors: "

View File

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

View File

@ -38,6 +38,7 @@ The main ``bash_it.sh`` script loads the frameworks individual components in the
* ``lib/composure.bash`` * ``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?) * Files in ``lib`` with the exception of ``appearance.bash`` - this means that ``composure.bash`` is loaded again here (possible improvement?)
* Enabled ``aliases`` * Enabled ``aliases``
* Enabled ``plugins`` * 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. 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 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,15 +1,15 @@
# shellcheck shell=bash
cite about-plugin cite about-plugin
about-plugin 'automatically set your xterm title with host and location info' about-plugin 'automatically set your xterm title with host and location info'
_short-dirname() { _short-dirname() {
local dir_name=`dirs +0` local dir_name=$(dirs +0)
[ "$SHORT_TERM_LINE" = true ] && [ ${#dir_name} -gt 8 ] && echo ${dir_name##*/} || echo $dir_name [ "$SHORT_TERM_LINE" = true ] && [ "${#dir_name}" -gt 8 ] && echo "${dir_name##*/}" || echo "${dir_name}"
} }
_short-command() { _short-command() {
local input_command="$@" local input_command="$*"
[ "$SHORT_TERM_LINE" = true ] && [ ${#input_command} -gt 8 ] && echo ${input_command%% *} || echo $input_command [ "$SHORT_TERM_LINE" = true ] && [ "${#input_command}" -gt 8 ] && echo "${input_command%% *}" || echo "${input_command}"
} }
set_xterm_title() { set_xterm_title() {
@ -17,14 +17,17 @@ set_xterm_title () {
echo -ne "\033]0;$title\007" echo -ne "\033]0;$title\007"
} }
precmd () { precmd_xterm_title() {
set_xterm_title "${SHORT_USER:-${USER}}@${SHORT_HOSTNAME:-${HOSTNAME}} `_short-dirname` $PROMPTCHAR" set_xterm_title "${SHORT_USER:-${USER}}@${SHORT_HOSTNAME:-${HOSTNAME}} $(_short-dirname) $PROMPT_CHAR"
} }
preexec () { preexec_xterm_title() {
set_xterm_title "`_short-command $1` {`_short-dirname`} (${SHORT_USER:-${USER}}@${SHORT_HOSTNAME:-${HOSTNAME}})" set_xterm_title "$(_short-command "${1}") {$(_short-dirname)} (${SHORT_USER:-${USER}}@${SHORT_HOSTNAME:-${HOSTNAME}})"
} }
case "$TERM" in case "$TERM" in
xterm*|rxvt*) preexec_install;; xterm* | rxvt*)
precmd_functions+=(precmd_xterm_title)
preexec_functions+=(preexec_xterm_title)
;;
esac 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 if [ -z "$BASH_IT_COMMAND_DURATION" ] || [ "$BASH_IT_COMMAND_DURATION" != true ]; then
_command_duration() { _command_duration() {
@ -66,8 +66,4 @@ _command_duration() {
fi fi
} }
preexec() ( preexec_functions+=(_command_duration_pre_exec)
_command_duration_pre_exec
)
preexec_install

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