1066 lines
29 KiB
Bash
1066 lines
29 KiB
Bash
# shellcheck shell=bash
|
|
# shellcheck disable=SC2016
|
|
#
|
|
# A collection of reusable functions.
|
|
|
|
: "${BASH_IT_LOAD_PRIORITY_DEFAULT_ALIAS:=150}"
|
|
: "${BASH_IT_LOAD_PRIORITY_DEFAULT_PLUGIN:=250}"
|
|
: "${BASH_IT_LOAD_PRIORITY_DEFAULT_COMPLETION:=350}"
|
|
BASH_IT_LOAD_PRIORITY_SEPARATOR="---"
|
|
|
|
# Handle the different ways of running `sed` without generating a backup file based on OS
|
|
# - GNU sed (Linux) uses `-i`
|
|
# - BSD sed (macOS) uses `-i ''`
|
|
# To use this in Bash-it for inline replacements with `sed`, use the following syntax:
|
|
# sed "${BASH_IT_SED_I_PARAMETERS[@]}" -e "..." file
|
|
BASH_IT_SED_I_PARAMETERS=('-i')
|
|
# shellcheck disable=SC2034 # expected for this case
|
|
case "$OSTYPE" in
|
|
'darwin'*) BASH_IT_SED_I_PARAMETERS=('-i' '') ;;
|
|
esac
|
|
|
|
function _command_exists() {
|
|
_about 'checks for existence of a command'
|
|
_param '1: command to check'
|
|
_param '2: (optional) log message to include when command not found'
|
|
_example '$ _command_exists ls && echo exists'
|
|
_group 'lib'
|
|
local msg="${2:-Command '$1' does not exist}"
|
|
if type -t "$1" > /dev/null; then
|
|
return 0
|
|
else
|
|
_log_debug "$msg"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
function _binary_exists() {
|
|
_about 'checks for existence of a binary'
|
|
_param '1: binary to check'
|
|
_param '2: (optional) log message to include when binary not found'
|
|
_example '$ _binary_exists ls && echo exists'
|
|
_group 'lib'
|
|
local msg="${2:-Binary '$1' does not exist}"
|
|
if type -P "$1" > /dev/null; then
|
|
return 0
|
|
else
|
|
_log_debug "$msg"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
function _completion_exists() {
|
|
_about 'checks for existence of a completion'
|
|
_param '1: command to check'
|
|
_param '2: (optional) log message to include when completion is found'
|
|
_example '$ _completion_exists gh && echo exists'
|
|
_group 'lib'
|
|
local msg="${2:-Completion for '$1' already exists}"
|
|
if complete -p "$1" &> /dev/null; then
|
|
_log_debug "$msg"
|
|
return 0
|
|
else
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
function _bash_it_homebrew_check() {
|
|
if _binary_exists 'brew'; then
|
|
# Homebrew is installed
|
|
if [[ "${BASH_IT_HOMEBREW_PREFIX:-unset}" == 'unset' ]]; then
|
|
# variable isn't set
|
|
BASH_IT_HOMEBREW_PREFIX="$(brew --prefix)"
|
|
else
|
|
true # Variable is set already, don't invoke `brew`.
|
|
fi
|
|
else
|
|
# Homebrew is not installed: clear variable.
|
|
BASH_IT_HOMEBREW_PREFIX=
|
|
false # return failure if brew not installed.
|
|
fi
|
|
}
|
|
|
|
function _make_reload_alias() {
|
|
echo "source '${BASH_IT?}/scripts/reloader.bash' '${1?}' '${2?}'"
|
|
}
|
|
|
|
# Alias for reloading aliases
|
|
# shellcheck disable=SC2139
|
|
alias reload_aliases="$(_make_reload_alias alias aliases)"
|
|
|
|
# Alias for reloading auto-completion
|
|
# shellcheck disable=SC2139
|
|
alias reload_completion="$(_make_reload_alias completion completion)"
|
|
|
|
# Alias for reloading plugins
|
|
# shellcheck disable=SC2139
|
|
alias reload_plugins="$(_make_reload_alias plugin plugins)"
|
|
|
|
function bash-it() {
|
|
about 'Bash-it help and maintenance'
|
|
param '1: verb [one of: help | show | enable | disable | migrate | update | search | version | reload | restart | doctor ] '
|
|
param '2: component type [one of: alias(es) | completion(s) | plugin(s) ] or search term(s)'
|
|
param '3: specific component [optional]'
|
|
example '$ bash-it show plugins'
|
|
example '$ bash-it help aliases'
|
|
example '$ bash-it enable plugin git [tmux]...'
|
|
example '$ bash-it disable alias hg [tmux]...'
|
|
example '$ bash-it migrate'
|
|
example '$ bash-it update'
|
|
example '$ bash-it search [-|@]term1 [-|@]term2 ... [ -e/--enable ] [ -d/--disable ] [ -r/--refresh ] [ -c/--no-color ]'
|
|
example '$ bash-it version'
|
|
example '$ bash-it reload'
|
|
example '$ bash-it restart'
|
|
example '$ bash-it profile list|save|load|rm [profile_name]'
|
|
example '$ bash-it doctor errors|warnings|all'
|
|
local verb=${1:-}
|
|
shift
|
|
local component=${1:-}
|
|
shift
|
|
local func
|
|
|
|
case $verb in
|
|
show)
|
|
func="_bash-it-$component"
|
|
;;
|
|
enable)
|
|
func="_enable-$component"
|
|
;;
|
|
disable)
|
|
func="_disable-$component"
|
|
;;
|
|
help)
|
|
func="_help-$component"
|
|
;;
|
|
doctor)
|
|
func="_bash-it-doctor-$component"
|
|
;;
|
|
profile)
|
|
func=_bash-it-profile-$component
|
|
;;
|
|
search)
|
|
_bash-it-search "$component" "$@"
|
|
return
|
|
;;
|
|
update)
|
|
func="_bash-it-update-$component"
|
|
;;
|
|
migrate)
|
|
func="_bash-it-migrate"
|
|
;;
|
|
version)
|
|
func="_bash-it-version"
|
|
;;
|
|
restart)
|
|
func="_bash-it-restart"
|
|
;;
|
|
reload)
|
|
func="_bash-it-reload"
|
|
;;
|
|
*)
|
|
reference "bash-it"
|
|
return
|
|
;;
|
|
esac
|
|
|
|
# pluralize component if necessary
|
|
if ! _is_function "$func"; then
|
|
if _is_function "${func}s"; then
|
|
func="${func}s"
|
|
else
|
|
if _is_function "${func}es"; then
|
|
func="${func}es"
|
|
else
|
|
echo "oops! $component is not a valid option!"
|
|
reference bash-it
|
|
return
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
if [[ "$verb" == "enable" || "$verb" == "disable" ]]; then
|
|
# Automatically run a migration if required
|
|
_bash-it-migrate
|
|
|
|
for arg in "$@"; do
|
|
"$func" "$arg"
|
|
done
|
|
|
|
if [[ -n "${BASH_IT_AUTOMATIC_RELOAD_AFTER_CONFIG_CHANGE:-}" ]]; then
|
|
_bash-it-reload
|
|
fi
|
|
else
|
|
"$func" "$@"
|
|
fi
|
|
}
|
|
|
|
function _is_function() {
|
|
_about 'sets $? to true if parameter is the name of a function'
|
|
_param '1: name of alleged function'
|
|
_param '2: (optional) log message to include when function not found'
|
|
_group 'lib'
|
|
_example '$ _is_function ls && echo exists'
|
|
_group 'lib'
|
|
local msg="${2:-Function '$1' does not exist}"
|
|
if LC_ALL=C type -t "$1" | _bash-it-egrep -q 'function'; then
|
|
return 0
|
|
else
|
|
_log_debug "$msg"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
function _bash-it-aliases() {
|
|
_about 'summarizes available bash_it aliases'
|
|
_group 'lib'
|
|
|
|
_bash-it-describe "aliases" "an" "alias" "Alias"
|
|
}
|
|
|
|
function _bash-it-completions() {
|
|
_about 'summarizes available bash_it completions'
|
|
_group 'lib'
|
|
|
|
_bash-it-describe "completion" "a" "completion" "Completion"
|
|
}
|
|
|
|
function _bash-it-plugins() {
|
|
_about 'summarizes available bash_it plugins'
|
|
_group 'lib'
|
|
|
|
_bash-it-describe "plugins" "a" "plugin" "Plugin"
|
|
}
|
|
|
|
function _bash-it-update-dev() {
|
|
_about 'updates Bash-it to the latest master'
|
|
_group 'lib'
|
|
|
|
_bash-it-update- dev "$@"
|
|
}
|
|
|
|
function _bash-it-update-stable() {
|
|
_about 'updates Bash-it to the latest tag'
|
|
_group 'lib'
|
|
|
|
_bash-it-update- stable "$@"
|
|
}
|
|
|
|
function _bash-it_update_migrate_and_restart() {
|
|
_about 'Checks out the wanted version, pops directory and restart. Does not return (because of the restart!)'
|
|
_param '1: Which branch to checkout to'
|
|
_param '2: Which type of version we are using'
|
|
if git checkout "$1" &> /dev/null; then
|
|
echo "Bash-it successfully updated."
|
|
echo ""
|
|
echo "Migrating your installation to the latest $2 version now..."
|
|
_bash-it-migrate
|
|
echo ""
|
|
echo "All done, enjoy!"
|
|
# Don't forget to restore the original pwd (called from `_bash-it-update-`)!
|
|
popd > /dev/null || return
|
|
_bash-it-restart
|
|
else
|
|
echo "Error updating Bash-it, please, check if your Bash-it installation folder (${BASH_IT}) is clean."
|
|
fi
|
|
}
|
|
|
|
function _bash-it-update-() {
|
|
_about 'updates Bash-it'
|
|
_param '1: What kind of update to do (stable|dev)'
|
|
_group 'lib'
|
|
|
|
declare silent
|
|
for word in "$@"; do
|
|
if [[ "${word}" == "--silent" || "${word}" == "-s" ]]; then
|
|
silent=true
|
|
fi
|
|
done
|
|
|
|
pushd "${BASH_IT?}" > /dev/null || return
|
|
|
|
DIFF=$(git diff --name-status)
|
|
if [[ -n "$DIFF" ]]; then
|
|
echo -e "Local changes detected in bash-it directory. Clean '$BASH_IT' directory to proceed.\n$DIFF"
|
|
return 1
|
|
fi
|
|
|
|
if [[ -z "$BASH_IT_REMOTE" ]]; then
|
|
BASH_IT_REMOTE="origin"
|
|
fi
|
|
|
|
git fetch "$BASH_IT_REMOTE" --tags &> /dev/null
|
|
|
|
if [[ -z "$BASH_IT_DEVELOPMENT_BRANCH" ]]; then
|
|
BASH_IT_DEVELOPMENT_BRANCH="master"
|
|
fi
|
|
# Defaults to stable update
|
|
if [[ -z "$1" || "$1" == "stable" ]]; then
|
|
version="stable"
|
|
TARGET=$(git describe --tags "$(git rev-list --tags --max-count=1)" 2> /dev/null)
|
|
|
|
if [[ -z "$TARGET" ]]; then
|
|
echo "Can not find tags, so can not update to latest stable version..."
|
|
popd > /dev/null || return
|
|
return
|
|
fi
|
|
else
|
|
version="dev"
|
|
TARGET="${BASH_IT_REMOTE}/${BASH_IT_DEVELOPMENT_BRANCH}"
|
|
fi
|
|
|
|
declare revision
|
|
revision="HEAD..${TARGET}"
|
|
declare status
|
|
status="$(git rev-list "${revision}" 2> /dev/null)"
|
|
declare revert
|
|
|
|
if [[ -z "${status}" && "${version}" == "stable" ]]; then
|
|
revision="${TARGET}..HEAD"
|
|
status="$(git rev-list "${revision}" 2> /dev/null)"
|
|
revert=true
|
|
fi
|
|
|
|
if [[ -n "${status}" ]]; then
|
|
if [[ -n "${revert}" ]]; then
|
|
echo "Your version is a more recent development version ($(git log -1 --format=%h HEAD))"
|
|
echo "You can continue in order to revert and update to the latest stable version"
|
|
echo ""
|
|
log_color="%Cred"
|
|
fi
|
|
|
|
for i in $(git rev-list --merges --first-parent "${revision}"); do
|
|
num_of_lines=$(git log -1 --format=%B "$i" | awk '!/^[[:space:]]*$ {++i} END{print i}')
|
|
if [[ "$num_of_lines" -eq 1 ]]; then
|
|
description="%s"
|
|
else
|
|
description="%b"
|
|
fi
|
|
git log --format="${log_color}%h: $description (%an)" -1 "$i"
|
|
done
|
|
echo ""
|
|
|
|
if [[ -n "${silent}" ]]; then
|
|
echo "Updating to ${TARGET}($(git log -1 --format=%h "${TARGET}"))..."
|
|
_bash-it_update_migrate_and_restart "$TARGET" "$version"
|
|
else
|
|
read -r -e -n 1 -p "Would you like to update to ${TARGET}($(git log -1 --format=%h "${TARGET}"))? [Y/n] " RESP
|
|
case "$RESP" in
|
|
[yY] | "")
|
|
_bash-it_update_migrate_and_restart "$TARGET" "$version"
|
|
;;
|
|
[nN])
|
|
echo "Not updating…"
|
|
;;
|
|
*)
|
|
echo -e "${echo_orange?}Please choose y or n.${echo_reset_color?}"
|
|
;;
|
|
esac
|
|
fi
|
|
else
|
|
if [[ "${version}" == "stable" ]]; then
|
|
echo "You're on the latest stable version. If you want to check out the latest 'dev' version, please run \"bash-it update dev\""
|
|
else
|
|
echo "Bash-it is up to date, nothing to do!"
|
|
fi
|
|
fi
|
|
popd > /dev/null || return
|
|
}
|
|
|
|
function _bash-it-migrate() {
|
|
_about 'migrates Bash-it configuration from a previous format to the current one'
|
|
_group 'lib'
|
|
|
|
local migrated_something component_type component_name single_type _bash_it_config_file
|
|
migrated_something=false
|
|
|
|
for file_type in "aliases" "plugins" "completion"; do
|
|
for _bash_it_config_file in "${BASH_IT}/$file_type/enabled"/*.bash; do
|
|
[[ -f "$_bash_it_config_file" ]] || continue
|
|
|
|
# Get the type of component from the extension
|
|
component_type="$(_bash-it-get-component-type-from-path "$_bash_it_config_file")"
|
|
# Cut off the optional "250---" prefix and the suffix
|
|
component_name="$(_bash-it-get-component-name-from-path "$_bash_it_config_file")"
|
|
|
|
migrated_something=true
|
|
|
|
single_type="${component_type/aliases/aliass}"
|
|
echo "Migrating ${single_type%s} $component_name."
|
|
|
|
disable_func="_disable-${single_type%s}"
|
|
enable_func="_enable-${single_type%s}"
|
|
|
|
"$disable_func" "$component_name"
|
|
"$enable_func" "$component_name"
|
|
done
|
|
done
|
|
|
|
if [[ -n "${BASH_IT_AUTOMATIC_RELOAD_AFTER_CONFIG_CHANGE:-}" ]]; then
|
|
_bash-it-reload
|
|
fi
|
|
|
|
if [[ "$migrated_something" == "true" ]]; then
|
|
echo ""
|
|
echo "If any migration errors were reported, please try the following: reload && bash-it migrate"
|
|
fi
|
|
}
|
|
|
|
function _bash-it-version() {
|
|
_about 'shows current Bash-it version'
|
|
_group 'lib'
|
|
|
|
pushd "${BASH_IT?}" > /dev/null || return
|
|
|
|
if [[ -z "${BASH_IT_REMOTE:-}" ]]; then
|
|
BASH_IT_REMOTE="origin"
|
|
fi
|
|
|
|
BASH_IT_GIT_REMOTE="$(git remote get-url "$BASH_IT_REMOTE")"
|
|
BASH_IT_GIT_URL="${BASH_IT_GIT_REMOTE%.git}"
|
|
if [[ "$BASH_IT_GIT_URL" == *"git@"* ]]; then
|
|
# Fix URL in case it is ssh based URL
|
|
BASH_IT_GIT_URL="${BASH_IT_GIT_URL/://}"
|
|
BASH_IT_GIT_URL="${BASH_IT_GIT_URL/git@/https://}"
|
|
fi
|
|
|
|
current_tag="$(git describe --exact-match --tags 2> /dev/null)"
|
|
|
|
if [[ -z "$current_tag" ]]; then
|
|
BASH_IT_GIT_VERSION_INFO="$(git log --pretty=format:'%h on %aI' -n 1)"
|
|
TARGET="${BASH_IT_GIT_VERSION_INFO%% *}"
|
|
echo "Version type: dev"
|
|
echo "Current git SHA: $BASH_IT_GIT_VERSION_INFO"
|
|
echo "Commit info: $BASH_IT_GIT_URL/commit/$TARGET"
|
|
else
|
|
TARGET="$current_tag"
|
|
echo "Version type: stable"
|
|
echo "Current tag: $current_tag"
|
|
echo "Tag information: $BASH_IT_GIT_URL/releases/tag/$current_tag"
|
|
fi
|
|
|
|
echo "Compare to latest: $BASH_IT_GIT_URL/compare/$TARGET...master"
|
|
|
|
popd > /dev/null || return
|
|
}
|
|
|
|
function _bash-it-doctor() {
|
|
_about 'reloads a profile file with a BASH_IT_LOG_LEVEL set'
|
|
_param '1: BASH_IT_LOG_LEVEL argument: "errors" "warnings" "all"'
|
|
_group 'lib'
|
|
|
|
# shellcheck disable=SC2034 # expected for this case
|
|
local BASH_IT_LOG_LEVEL="${1?}"
|
|
_bash-it-reload
|
|
}
|
|
|
|
function _bash-it-doctor-all() {
|
|
_about 'reloads a profile file with error, warning and debug logs'
|
|
_group 'lib'
|
|
|
|
_bash-it-doctor "${BASH_IT_LOG_LEVEL_ALL?}"
|
|
}
|
|
|
|
function _bash-it-doctor-warnings() {
|
|
_about 'reloads a profile file with error and warning logs'
|
|
_group 'lib'
|
|
|
|
_bash-it-doctor "${BASH_IT_LOG_LEVEL_WARNING?}"
|
|
}
|
|
|
|
function _bash-it-doctor-errors() {
|
|
_about 'reloads a profile file with error logs'
|
|
_group 'lib'
|
|
|
|
_bash-it-doctor "${BASH_IT_LOG_LEVEL_ERROR?}"
|
|
}
|
|
|
|
function _bash-it-doctor-() {
|
|
_about 'default bash-it doctor behavior, behaves like bash-it doctor all'
|
|
_group 'lib'
|
|
|
|
_bash-it-doctor-all
|
|
}
|
|
|
|
function _bash-it-profile-save() {
|
|
_about 'saves the current configuration to the "profile" directory'
|
|
_group 'lib'
|
|
|
|
local name="${1:-}"
|
|
while [[ -z "$name" ]]; do
|
|
read -r -e -p "Please enter the name of the profile to save: " name
|
|
case $name in
|
|
"")
|
|
echo -e "${echo_orange?}Please choose a name.${echo_reset_color?}"
|
|
;;
|
|
*)
|
|
break
|
|
;;
|
|
esac
|
|
done
|
|
|
|
local profile_path="${BASH_IT}/profiles/${name}.bash_it" RESP
|
|
if [[ -s "$profile_path" ]]; then
|
|
echo -e "${echo_yellow?}Profile \"$name\" already exists.${echo_reset_color?}"
|
|
while true; do
|
|
read -r -e -n 1 -p "Would you like to overwrite existing profile? [y/N] " RESP
|
|
case $RESP in
|
|
[yY])
|
|
echo -e "${echo_green?}Overwriting profile \"$name\"...${echo_reset_color?}"
|
|
rm "$profile_path"
|
|
break
|
|
;;
|
|
[nN] | "")
|
|
echo -e "${echo_orange?}Aborting profile save...${echo_reset_color?}"
|
|
return 1
|
|
;;
|
|
*)
|
|
echo -e "${echo_orange?}Please choose y or n.${echo_reset_color?}"
|
|
;;
|
|
esac
|
|
done
|
|
fi
|
|
|
|
local something_exists subdirectory
|
|
echo "# This file is auto generated by Bash-it. Do not edit manually!" > "$profile_path"
|
|
for subdirectory in "plugins" "completion" "aliases"; do
|
|
local component_exists="" f
|
|
echo "Saving $subdirectory configuration..."
|
|
for f in "${BASH_IT}/$subdirectory/available/"*.bash; do
|
|
_bash-it-determine-component-status-from-path "$f"
|
|
if [[ "$enabled" == "x" ]]; then
|
|
if [[ -z "$component_exists" ]]; then
|
|
# This is the first component of this type, print the header
|
|
component_exists="yes"
|
|
something_exists="yes"
|
|
echo "" >> "$profile_path"
|
|
echo "# $subdirectory" >> "$profile_path"
|
|
fi
|
|
echo "$subdirectory $enabled_file" >> "$profile_path"
|
|
fi
|
|
done
|
|
done
|
|
if [[ -z "$something_exists" ]]; then
|
|
echo "It seems like no configuration was enabled.."
|
|
echo "Make sure to double check that this is the wanted behavior."
|
|
fi
|
|
|
|
echo "All done!"
|
|
echo ""
|
|
echo "Profile location: $profile_path"
|
|
echo "Load the profile by invoking \"bash-it profile load $name\""
|
|
}
|
|
|
|
_bash-it-profile-load-parse-profile() {
|
|
_about 'Internal function used to parse the profile file'
|
|
_param '1: path to the profile file'
|
|
_param '2: dry run- only check integrity of the profile file'
|
|
_example '$ _bash-it-profile-load-parse-profile "profile.bash_it" "dry"'
|
|
|
|
local -i num=0
|
|
local line
|
|
while read -r -a line; do
|
|
((++num))
|
|
# Ignore comments and empty lines
|
|
[[ -z "${line[*]}" || "${line[*]}" =~ ^#.* ]] && continue
|
|
local enable_func="_enable-${line[0]}"
|
|
local subdirectory=${line[0]}
|
|
local component=${line[1]}
|
|
|
|
local to_enable=("${BASH_IT}/$subdirectory/available/$component.${subdirectory%s}"*.bash)
|
|
# Ignore botched lines
|
|
if [[ ! -e "${to_enable[0]}" ]]; then
|
|
echo -e "${echo_orange?}Bad line(#$num) in profile, aborting load...${line[*]}${echo_reset_color?}"
|
|
local bad="bad line"
|
|
break
|
|
fi
|
|
# Do not actually modify config on dry run
|
|
[[ -z $2 ]] || continue
|
|
# Actually enable the component
|
|
$enable_func "$component"
|
|
done < "$1"
|
|
|
|
# Make sure to propagate the error
|
|
[[ -z $bad ]]
|
|
}
|
|
|
|
_bash-it-profile-list() {
|
|
about 'lists all profiles from the "profiles" directory'
|
|
_group 'lib'
|
|
local profile
|
|
|
|
echo "Available profiles:"
|
|
for profile in "${BASH_IT}/profiles"/*.bash_it; do
|
|
profile="${profile##*/}"
|
|
echo "${profile/.bash_it/}"
|
|
done
|
|
}
|
|
|
|
_bash-it-profile-rm() {
|
|
about 'Removes a profile from the "profiles" directory'
|
|
_group 'lib'
|
|
|
|
local name="$1"
|
|
if [[ -z $name ]]; then
|
|
echo -e "${echo_orange?}Please specify profile name to remove...${echo_reset_color?}"
|
|
return 1
|
|
fi
|
|
|
|
# Users should not be allowed to delete the default profile
|
|
if [[ $name == "default" ]]; then
|
|
echo -e "${echo_orange?}Can not remove the default profile...${echo_reset_color?}"
|
|
return 1
|
|
fi
|
|
|
|
local profile_path="${BASH_IT}/profiles/$name.bash_it"
|
|
if [[ ! -f "$profile_path" ]]; then
|
|
echo -e "${echo_orange?}Could not find profile '$name'...${echo_reset_color?}"
|
|
return 1
|
|
fi
|
|
|
|
command rm "$profile_path"
|
|
echo "Removed profile '$name' successfully!"
|
|
}
|
|
|
|
_bash-it-profile-load() {
|
|
_about 'loads a configuration from the "profiles" directory'
|
|
_group 'lib'
|
|
|
|
local name="$1"
|
|
if [[ -z $name ]]; then
|
|
echo -e "${echo_orange?}Please specify profile name to load, not changing configuration...${echo_reset_color?}"
|
|
return 1
|
|
fi
|
|
|
|
local profile_path="${BASH_IT}/profiles/$name.bash_it"
|
|
if [[ ! -f "$profile_path" ]]; then
|
|
echo -e "${echo_orange?}Could not find profile '$name', not changing configuration...${echo_reset_color?}"
|
|
return 1
|
|
fi
|
|
|
|
echo "Trying to parse profile '$name'..."
|
|
if _bash-it-profile-load-parse-profile "$profile_path" "dry"; then
|
|
echo "Profile '$name' parsed successfully!"
|
|
echo "Disabling current configuration..."
|
|
_disable-all
|
|
echo ""
|
|
echo "Enabling configuration based on profile..."
|
|
_bash-it-profile-load-parse-profile "$profile_path"
|
|
echo ""
|
|
echo "Profile '$name' enabled!"
|
|
else
|
|
false # failure
|
|
fi
|
|
}
|
|
|
|
function _bash-it-restart() {
|
|
_about 'restarts the shell in order to fully reload it'
|
|
_group 'lib'
|
|
|
|
saved_pwd="${PWD}"
|
|
|
|
if shopt -q login_shell; then
|
|
init_file=.bash_profile
|
|
else
|
|
init_file=.bashrc
|
|
fi
|
|
exec "${0/-/}" --rcfile <(echo "source \"$HOME/$init_file\"; cd \"$saved_pwd\"")
|
|
}
|
|
|
|
function _bash-it-reload() {
|
|
_about 'reloads a profile file'
|
|
_group 'lib'
|
|
|
|
pushd "${BASH_IT?}" > /dev/null || return
|
|
|
|
# shellcheck disable=SC1090
|
|
if shopt -q login_shell; then
|
|
# shellcheck source-path=$HOME
|
|
source ~/.bash_profile
|
|
else
|
|
# shellcheck source-path=$HOME
|
|
source ~/.bashrc
|
|
fi
|
|
popd > /dev/null || return
|
|
}
|
|
|
|
_bash-it-determine-component-status-from-path() {
|
|
_about 'internal function used to process component name and check if its enabled'
|
|
_param '1: full path to available component file'
|
|
_example '$ _bash-it-determine-component-status-from-path "${BASH_IT}/plugins/available/git.plugin.bash'
|
|
|
|
# Check for both the old format without the load priority, and the extended format with the priority
|
|
local enabled_files enabled_file
|
|
enabled_file="${f##*/}"
|
|
enabled_file="${enabled_file%."${file_type}"*.bash}"
|
|
enabled=
|
|
_bash-it-component-item-is-enabled "${file_type}" "${enabled_file}" && enabled='x'
|
|
return 0
|
|
}
|
|
|
|
function _bash-it-describe() {
|
|
_about 'summarizes available bash_it components'
|
|
_param '1: subdirectory'
|
|
_param '2: preposition'
|
|
_param '3: file_type'
|
|
_param '4: column_header'
|
|
_example '$ _bash-it-describe "plugins" "a" "plugin" "Plugin"'
|
|
|
|
subdirectory="$1"
|
|
preposition="$2"
|
|
file_type="$3"
|
|
column_header="$4"
|
|
|
|
local f
|
|
local enabled enabled_file
|
|
printf "%-20s %-10s %s\n" "$column_header" 'Enabled?' 'Description'
|
|
for f in "${BASH_IT?}/$subdirectory/available"/*.*.bash; do
|
|
_bash-it-determine-component-status-from-path "$f"
|
|
printf "%-20s %-10s %s\n" "$enabled_file" "[${enabled:- }]" "$(metafor "about-$file_type" < "$f")"
|
|
done
|
|
printf '\n%s\n' "to enable $preposition $file_type, do:"
|
|
printf '%s\n' "$ bash-it enable $file_type <$file_type name> [$file_type name]... -or- $ bash-it enable $file_type all"
|
|
printf '\n%s\n' "to disable $preposition $file_type, do:"
|
|
printf '%s\n' "$ bash-it disable $file_type <$file_type name> [$file_type name]... -or- $ bash-it disable $file_type all"
|
|
}
|
|
|
|
function _on-disable-callback() {
|
|
_about 'Calls the disabled plugin destructor, if present'
|
|
_param '1: plugin name'
|
|
_example '$ _on-disable-callback gitstatus'
|
|
_group 'lib'
|
|
|
|
callback="${1}_on_disable"
|
|
_command_exists "$callback" && "$callback"
|
|
}
|
|
|
|
function _disable-all() {
|
|
_about 'disables all bash_it components'
|
|
_example '$ _disable-all'
|
|
_group 'lib'
|
|
|
|
_disable-plugin "all"
|
|
_disable-alias "all"
|
|
_disable-completion "all"
|
|
}
|
|
|
|
function _disable-plugin() {
|
|
_about 'disables bash_it plugin'
|
|
_param '1: plugin name'
|
|
_example '$ disable-plugin rvm'
|
|
_group 'lib'
|
|
|
|
_disable-thing "plugins" "plugin" "$1"
|
|
_on-disable-callback "$1"
|
|
}
|
|
|
|
function _disable-alias() {
|
|
_about 'disables bash_it alias'
|
|
_param '1: alias name'
|
|
_example '$ disable-alias git'
|
|
_group 'lib'
|
|
|
|
_disable-thing "aliases" "alias" "$1"
|
|
}
|
|
|
|
function _disable-completion() {
|
|
_about 'disables bash_it completion'
|
|
_param '1: completion name'
|
|
_example '$ disable-completion git'
|
|
_group 'lib'
|
|
|
|
_disable-thing "completion" "completion" "$1"
|
|
}
|
|
|
|
function _disable-thing() {
|
|
_about 'disables a bash_it component'
|
|
_param '1: subdirectory'
|
|
_param '2: file_type'
|
|
_param '3: file_entity'
|
|
_example '$ _disable-thing "plugins" "plugin" "ssh"'
|
|
|
|
local subdirectory="${1?}"
|
|
local file_type="${2?}"
|
|
local file_entity="${3:-}"
|
|
|
|
if [[ -z "$file_entity" ]]; then
|
|
reference "disable-$file_type"
|
|
return
|
|
fi
|
|
|
|
local f suffix _bash_it_config_file plugin
|
|
suffix="${subdirectory/plugins/plugin}"
|
|
|
|
if [[ "$file_entity" == "all" ]]; then
|
|
# Disable everything that's using the old structure and everything in the global "enabled" directory.
|
|
for _bash_it_config_file in "${BASH_IT}/$subdirectory/enabled"/*."${suffix}.bash" "${BASH_IT}/enabled"/*".${suffix}.bash"; do
|
|
rm -f "$_bash_it_config_file"
|
|
done
|
|
else
|
|
# Use a glob to search for both possible patterns
|
|
# 250---node.plugin.bash
|
|
# node.plugin.bash
|
|
# Either one will be matched by this glob
|
|
for plugin in "${BASH_IT}/enabled"/[[:digit:]][[:digit:]][[:digit:]]"${BASH_IT_LOAD_PRIORITY_SEPARATOR}${file_entity}.${suffix}.bash" "${BASH_IT}/$subdirectory/enabled/"{[[:digit:]][[:digit:]][[:digit:]]"${BASH_IT_LOAD_PRIORITY_SEPARATOR}${file_entity}.${suffix}.bash","${file_entity}.${suffix}.bash"}; do
|
|
if [[ -e "${plugin}" ]]; then
|
|
rm "${plugin}"
|
|
plugin=
|
|
break
|
|
fi
|
|
done
|
|
if [[ -n "${plugin}" ]]; then
|
|
printf '%s\n' "sorry, $file_entity does not appear to be an enabled $file_type."
|
|
return
|
|
fi
|
|
fi
|
|
|
|
_bash-it-clean-component-cache "${file_type}"
|
|
|
|
if [[ "$file_entity" = "all" ]]; then
|
|
printf '%s\n' "$file_entity $(_bash-it-pluralize-component "$file_type") disabled."
|
|
else
|
|
printf '%s\n' "$file_entity disabled."
|
|
fi
|
|
}
|
|
|
|
function _enable-plugin() {
|
|
_about 'enables bash_it plugin'
|
|
_param '1: plugin name'
|
|
_example '$ enable-plugin rvm'
|
|
_group 'lib'
|
|
|
|
_enable-thing "plugins" "plugin" "$1" "$BASH_IT_LOAD_PRIORITY_DEFAULT_PLUGIN"
|
|
}
|
|
|
|
function _enable-plugins() {
|
|
_about 'alias of _enable-plugin'
|
|
_enable-plugin "$@"
|
|
}
|
|
|
|
function _enable-alias() {
|
|
_about 'enables bash_it alias'
|
|
_param '1: alias name'
|
|
_example '$ enable-alias git'
|
|
_group 'lib'
|
|
|
|
_enable-thing "aliases" "alias" "$1" "$BASH_IT_LOAD_PRIORITY_DEFAULT_ALIAS"
|
|
}
|
|
|
|
function _enable-aliases() {
|
|
_about 'alias of _enable-alias'
|
|
_enable-alias "$@"
|
|
}
|
|
|
|
function _enable-completion() {
|
|
_about 'enables bash_it completion'
|
|
_param '1: completion name'
|
|
_example '$ enable-completion git'
|
|
_group 'lib'
|
|
|
|
_enable-thing "completion" "completion" "$1" "$BASH_IT_LOAD_PRIORITY_DEFAULT_COMPLETION"
|
|
}
|
|
|
|
function _enable-thing() {
|
|
cite _about _param _example
|
|
_about 'enables a bash_it component'
|
|
_param '1: subdirectory'
|
|
_param '2: file_type'
|
|
_param '3: file_entity'
|
|
_param '4: load priority'
|
|
_example '$ _enable-thing "plugins" "plugin" "ssh" "150"'
|
|
|
|
local subdirectory="${1?}"
|
|
local file_type="${2?}"
|
|
local file_entity="${3:-}"
|
|
local load_priority="${4:-500}"
|
|
|
|
if [[ -z "$file_entity" ]]; then
|
|
reference "enable-$file_type"
|
|
return
|
|
fi
|
|
|
|
local _bash_it_config_file to_enable to_enables enabled_plugin local_file_priority use_load_priority
|
|
local suffix="${subdirectory/plugins/plugin}"
|
|
|
|
if [[ "$file_entity" == "all" ]]; then
|
|
for _bash_it_config_file in "${BASH_IT}/$subdirectory/available"/*.bash; do
|
|
to_enable="${_bash_it_config_file##*/}"
|
|
_enable-thing "$subdirectory" "$file_type" "${to_enable%."${file_type/alias/aliases}".bash}" "$load_priority"
|
|
done
|
|
else
|
|
to_enables=("${BASH_IT}/$subdirectory/available/$file_entity.${suffix}.bash")
|
|
if [[ ! -e "${to_enables[0]}" ]]; then
|
|
printf '%s\n' "sorry, $file_entity does not appear to be an available $file_type."
|
|
return
|
|
fi
|
|
|
|
to_enable="${to_enables[0]##*/}"
|
|
# Check for existence of the file using a wildcard, since we don't know which priority might have been used when enabling it.
|
|
for enabled_plugin in "${BASH_IT}/$subdirectory/enabled"/{[[:digit:]][[:digit:]][[:digit:]]"${BASH_IT_LOAD_PRIORITY_SEPARATOR}${to_enable}","${to_enable}"} "${BASH_IT}/enabled"/[[:digit:]][[:digit:]][[:digit:]]"${BASH_IT_LOAD_PRIORITY_SEPARATOR?}${to_enable}"; do
|
|
if [[ -e "${enabled_plugin}" ]]; then
|
|
printf '%s\n' "$file_entity is already enabled."
|
|
return
|
|
fi
|
|
done
|
|
|
|
mkdir -p "${BASH_IT}/enabled"
|
|
|
|
# Load the priority from the file if it present there
|
|
local_file_priority="$(_bash-it-egrep "^# BASH_IT_LOAD_PRIORITY:" "${BASH_IT}/$subdirectory/available/$to_enable" | awk -F': ' '{ print $2 }')"
|
|
use_load_priority="${local_file_priority:-$load_priority}"
|
|
|
|
ln -s "../$subdirectory/available/$to_enable" "${BASH_IT}/enabled/${use_load_priority}${BASH_IT_LOAD_PRIORITY_SEPARATOR}${to_enable}"
|
|
fi
|
|
|
|
_bash-it-clean-component-cache "${file_type}"
|
|
|
|
printf '%s\n' "$file_entity enabled with priority $use_load_priority."
|
|
}
|
|
|
|
function _help-completions() {
|
|
_about 'summarize all completions available in bash-it'
|
|
_group 'lib'
|
|
|
|
_bash-it-completions
|
|
}
|
|
|
|
function _help-aliases() {
|
|
_about 'shows help for all aliases, or a specific alias group'
|
|
_param '1: optional alias group'
|
|
_example '$ alias-help'
|
|
_example '$ alias-help git'
|
|
|
|
if [[ -n "$1" ]]; then
|
|
case $1 in
|
|
custom)
|
|
alias_path='custom.aliases.bash'
|
|
;;
|
|
*)
|
|
alias_path="available/${1}.aliases.bash"
|
|
;;
|
|
esac
|
|
metafor alias < "${BASH_IT}/aliases/$alias_path" | sed "s/$/'/"
|
|
else
|
|
local f
|
|
|
|
for f in "${BASH_IT}/aliases/enabled"/* "${BASH_IT}/enabled"/*."aliases.bash"; do
|
|
[[ -f "$f" ]] || continue
|
|
_help-list-aliases "$f"
|
|
done
|
|
|
|
if [[ -e "${BASH_IT}/aliases/custom.aliases.bash" ]]; then
|
|
_help-list-aliases "${BASH_IT}/aliases/custom.aliases.bash"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
function _help-list-aliases() {
|
|
local file
|
|
file="$(_bash-it-get-component-name-from-path "${1?}")"
|
|
printf '\n\n%s:\n' "${file}"
|
|
# metafor() strips trailing quotes, restore them with sed..
|
|
metafor alias < "$1" | sed "s/$/'/"
|
|
}
|
|
|
|
function _help-plugins() {
|
|
_about 'summarize all functions defined by enabled bash-it plugins'
|
|
_group 'lib'
|
|
|
|
local grouplist func group about gfile
|
|
# display a brief progress message...
|
|
printf '%s' 'please wait, building help...'
|
|
grouplist="$(mktemp -t grouplist.XXXXXX)"
|
|
for func in $(_typeset_functions); do
|
|
group="$(declare -f "$func" | metafor group)"
|
|
if [[ -z "$group" ]]; then
|
|
group='misc'
|
|
fi
|
|
about="$(declare -f "$func" | metafor about)"
|
|
_letterpress "$about" "$func" >> "$grouplist.$group"
|
|
echo "$grouplist.$group" >> "$grouplist"
|
|
done
|
|
# clear progress message
|
|
printf '\r%s\n' ' '
|
|
while IFS= read -r gfile; do
|
|
printf '%s\n' "${gfile##*.}:"
|
|
cat "$gfile"
|
|
printf '\n'
|
|
rm "$gfile" 2> /dev/null
|
|
done < <(sort -u "$grouplist") | less
|
|
rm "$grouplist" 2> /dev/null
|
|
}
|
|
|
|
function _help-profile() {
|
|
_about 'help message for profile command'
|
|
_group 'lib'
|
|
|
|
echo "Manages profiles of bash it."
|
|
echo "Use 'bash-it profile list' to see all available profiles."
|
|
echo "Use 'bash-it profile save foo' to save the current configuration into a profile named 'foo'."
|
|
echo "Use 'bash-it profile load foo' to load an existing profile named 'foo'."
|
|
echo "Use 'bash-it profile rm foo' to remove an existing profile named 'foo'."
|
|
}
|
|
|
|
function _help-update() {
|
|
_about 'help message for update command'
|
|
_group 'lib'
|
|
|
|
echo "Check for a new version of Bash-it and update it."
|
|
}
|
|
|
|
function _help-migrate() {
|
|
_about 'help message for migrate command'
|
|
_group 'lib'
|
|
|
|
echo "Migrates internal Bash-it structure to the latest version in case of changes."
|
|
echo "The 'migrate' command is run automatically when calling 'update', 'enable' or 'disable'."
|
|
}
|
|
|
|
function all_groups() {
|
|
about 'displays all unique metadata groups'
|
|
group 'lib'
|
|
|
|
declare -f | metafor group | sort -u
|
|
}
|
|
|
|
function pathmunge() {
|
|
about 'prevent duplicate directories in you PATH variable'
|
|
group 'helpers'
|
|
example 'pathmunge /path/to/dir is equivalent to PATH=/path/to/dir:$PATH'
|
|
example 'pathmunge /path/to/dir after is equivalent to PATH=$PATH:/path/to/dir'
|
|
|
|
if [[ -d "${1:-}" && ! $PATH =~ (^|:)"${1}"($|:) ]]; then
|
|
if [[ "${2:-}" == "after" ]]; then
|
|
export PATH="$PATH:${1}"
|
|
else
|
|
export PATH="${1}:$PATH"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# `_bash-it-find-in-ancestor` uses the shell's ability to run a function in
|
|
# a subshell to simplify our search to a simple `cd ..` and `[[ -r $1 ]]`
|
|
# without any external dependencies. Let the shell do what it's good at.
|
|
function _bash-it-find-in-ancestor() (
|
|
about 'searches parents of the current directory for any of the specified file names'
|
|
group 'helpers'
|
|
param '*: names of files or folders to search for'
|
|
returns '0: prints path of closest matching ancestor directory to stdout'
|
|
returns '1: no match found'
|
|
returns '2: improper usage of shell builtin' # uncommon
|
|
example '_bash-it-find-in-ancestor .git .hg'
|
|
example '_bash-it-find-in-ancestor GNUmakefile Makefile makefile'
|
|
|
|
local kin
|
|
# To keep things simple, we do not search the root dir.
|
|
while [[ "${PWD}" != '/' ]]; do
|
|
for kin in "$@"; do
|
|
if [[ -r "${PWD}/${kin}" ]]; then
|
|
printf '%s' "${PWD}"
|
|
return "$?"
|
|
fi
|
|
done
|
|
command cd .. || return "$?"
|
|
done
|
|
return 1
|
|
)
|