When the file is being re-created, we write to it, instead of appending to it. So, the rm here is unnecessary and prevents users from linking the profile file to another location that is potentially under version control. For instance, once could link to a profile file located at "$BASH_IT_CUSTOM/profiles/*.bash_it".
1046 lines
29 KiB
Bash
1046 lines
29 KiB
Bash
# shellcheck shell=bash
|
|
# shellcheck disable=SC2016
|
|
#
|
|
# A collection of reusable functions.
|
|
|
|
: "${BASH_IT_LOAD_PRIORITY_ALIAS:=150}"
|
|
: "${BASH_IT_LOAD_PRIORITY_PLUGIN:=250}"
|
|
: "${BASH_IT_LOAD_PRIORITY_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 | preview | 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 preview'
|
|
example '$ bash-it preview essential'
|
|
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
|
|
;;
|
|
preview)
|
|
_bash-it-preview "$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'
|
|
|
|
local silent word DIFF version TARGET revision status revert log_color num_of_lines description i RESP
|
|
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
|
|
|
|
revision="HEAD..${TARGET}"
|
|
status="$(git rev-list "${revision}" 2> /dev/null)"
|
|
|
|
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 file_type _bash_it_config_file disable_func enable_func
|
|
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'
|
|
|
|
local BASH_IT_GIT_REMOTE BASH_IT_GIT_URL current_tag BASH_IT_GIT_VERSION_INFO TARGET
|
|
|
|
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?}"
|
|
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 component_exists f enabled_file
|
|
echo "# This file is auto generated by Bash-it. Do not edit manually!" > "$profile_path"
|
|
for subdirectory in "plugins" "completion" "aliases"; do
|
|
echo "Saving $subdirectory configuration..."
|
|
for f in "${BASH_IT}/$subdirectory/available"/*.bash; do
|
|
if _bash-it-component-item-is-enabled "$f"; 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
|
|
enabled_file="$(_bash-it-get-component-name-from-path "$f")"
|
|
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'
|
|
|
|
local saved_pwd="${PWD}" init_file="${BASH_IT_BASHRC:-${HOME?}/.bashrc}"
|
|
|
|
exec "${0/-/}" --rcfile <(echo "source \"${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
|
|
source "${BASH_IT_BASHRC:-${HOME?}/.bashrc}"
|
|
popd > /dev/null || return
|
|
}
|
|
|
|
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"'
|
|
|
|
local subdirectory preposition file_type column_header f enabled enabled_file
|
|
subdirectory="$1"
|
|
preposition="$2"
|
|
file_type="$3"
|
|
column_header="$4"
|
|
|
|
printf "%-20s %-10s %s\n" "$column_header" 'Enabled?' 'Description'
|
|
for f in "${BASH_IT?}/$subdirectory/available"/*.*.bash; do
|
|
enabled=''
|
|
enabled_file="${f##*/}"
|
|
enabled_file="${enabled_file%."${file_type}"*.bash}"
|
|
_bash-it-component-item-is-enabled "${file_type}" "${enabled_file}" && enabled='x'
|
|
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'
|
|
|
|
local 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_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_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_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="$(awk -F': ' '$1 == "# BASH_IT_LOAD_PRIORITY" { print $2 }' "${BASH_IT}/$subdirectory/available/$to_enable")"
|
|
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 defn
|
|
# display a brief progress message...
|
|
printf '%s' 'please wait, building help...'
|
|
grouplist="$(mktemp -t grouplist.XXXXXX)"
|
|
while read -ra func; do
|
|
defn="$(declare -f "${func[2]}")"
|
|
group="$(metafor group <<< "$defn")"
|
|
if [[ -z "$group" ]]; then
|
|
group='misc'
|
|
fi
|
|
about="$(metafor about <<< "$defn")"
|
|
_letterpress "$about" "${func[2]}" >> "$grouplist.$group"
|
|
echo "$grouplist.$group" >> "$grouplist"
|
|
done < <(declare -F)
|
|
# 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 your 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:-before}" == "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
|
|
)
|