diff --git a/.editorconfig b/.editorconfig old mode 100755 new mode 100644 index e0eb8455..f67aa2df --- a/.editorconfig +++ b/.editorconfig @@ -13,17 +13,24 @@ insert_final_newline = true trim_trailing_whitespace = false [.git*] +indent_size = tab indent_style = tab -[{**.*sh,test/run}] +[{**.*sh,test/run,**.bats}] +indent_size = tab indent_style = tab shell_variant = bash binary_next_line = true # like -bn switch_case_indent = true # like -ci space_redirects = true # like -sr -keep_padding = false # like -kp +keep_padding = false # like -kp end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true + +[**.bats] +indent_size = tab +indent_style = tab +shell_variant = bats diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index b501d941..00000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -name: Bug report -about: Create a bug report to help us improve -title: '' -labels: bug:general -assignees: '' - ---- - - - -## Expected Behavior - - -## Current Behavior - - -## Possible Solution - - -## Context - - - -## Steps to Reproduce - - -1. -2. -3. -4. - -## Your Environment - -* Bash-it version used: -* List of enabled plugins, themes and aliases (use ``bash-it show (plugins/themes/aliases)``): -* ``bash-it doctor`` output: -* Bash version: -* Operating System and version: - -## Your Bash Config File - - -```bash - # Your bash config file should be here -``` diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 00000000..a187422e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,85 @@ +name: 🐛 Bug report +title: "[Bug]: " +description: Create a bug report to help us improve +labels: "bug:general" +body: + - type: textarea + attributes: + label: Expected behavior + description: Tell us what should happen. + validations: + required: true + - type: textarea + attributes: + label: Current behavior + description: Tell us what happens instead of the expected behavior. + validations: + required: true + - type: textarea + attributes: + label: Possible solution + description: Tell us how it could be fixed at your glance. + validations: + required: false + - type: textarea + attributes: + label: Context + description: > + How has this issue affected you? What are you trying to accomplish? + Providing context helps us come up with a solution that is most useful in the real world. + validations: + required: false + - type: textarea + attributes: + label: Steps to reproduce + description: > + Provide a link to a live example, or an unambiguous set of steps to reproduce this bug. Include code to reproduce, if relevant. + validations: + required: true + - type: input + attributes: + label: Bash-it version + placeholder: "How to get: bash-it version" + validations: + required: true + - type: input + attributes: + label: List of enabled plugins, themes and aliases + placeholder: "How to get: bash-it show plugins|themes|aliases (it is not a pipe)" + validations: + required: true + - type: input + attributes: + label: Bash version + placeholder: "How to get: bash --version" + validations: + required: true + - type: input + attributes: + label: Operating system and version + placeholder: "How to get: neofetch (or another command)" + validations: + required: true + - type: textarea + attributes: + label: "bash-it doctor output" + value: | + ``` + # How to get: bash-it doctor + ``` + validations: + required: false + - type: textarea + attributes: + label: Your ~/.bashrc + value: | + ```bash + # How to get: cat ~/.bashrc + ``` + validations: + required: true + - type: textarea + attributes: + label: Notes + description: > + Provide any extra details here. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000..f33f8a3f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: true +contact_links: + - name: Libera chat + url: https://web.libera.chat/?channel=#bash-it + about: You can ask and answer questions here diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index b1fabfba..00000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for this project -title: '' -labels: feature request -assignees: '' - ---- - - - -## Expected Behavior - - -## Current Behavior - - -## Possible Solution - - -## Context - - diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 00000000..670aef64 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,34 @@ +name: 💡 Feature request +title: "[Feature]: " +description: Suggest an idea for this project +labels: "feature request" +body: + - type: textarea + attributes: + label: Expected behavior + description: Tell us how your feature should work. + validations: + required: true + - type: textarea + attributes: + label: Current behavior + description: Explain the difference your feature will have from current behavior. + validations: + required: true + - type: textarea + attributes: + label: Possible solution + description: Tell us how it could be fixed at your glance. + validations: + required: false + - type: textarea + attributes: + label: Context + description: > + How has this issue affected you? What are you trying to accomplish? + Providing context helps us come up with a solution that is most useful in the real world. + - type: textarea + attributes: + label: Notes + description: > + Provide any extra details here. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 03b75882..0eee145c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,17 +1,13 @@ name: CI # Triggers the workflow on push or pull request events -on: - push: - branches: [ master ] - pull_request: - branches: [ master ] +on: [push, pull_request] jobs: bats-test: strategy: matrix: - os: [ubuntu-20.04, ubuntu-18.04, ubuntu-16.04, macos-10.15, macos-11.0] + os: [ubuntu-20.04, ubuntu-18.04, macos-10.15, macos-11] runs-on: ${{ matrix.os }} @@ -20,6 +16,9 @@ jobs: - name: Install greadlink if: startsWith(runner.os, 'macOS') run: brew install coreutils + - name: Install parallel + if: startsWith(runner.os, 'macOS') + run: brew install parallel - name: Test code run: test/run @@ -45,7 +44,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v2 with: - go-version: 1.14 + go-version: 1.17 - name: Set up Python uses: actions/setup-python@v2 with: diff --git a/.gitignore b/.gitignore old mode 100755 new mode 100644 index a17b6e82..8e6f12a1 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,8 @@ bats enabled/* /enabled tmp/ + +# Do not save profiles +profiles/* +# apart from the default one +!profiles/default.bash_it diff --git a/.shellcheckrc b/.shellcheckrc deleted file mode 100644 index 6418bd8f..00000000 --- a/.shellcheckrc +++ /dev/null @@ -1,6 +0,0 @@ -# We use colors and not assigned -disable=SC2154 -# Hard to fix -disable=SC2155 -# shellcheck is wrong on some -disable=SC2034 diff --git a/aliases/available/ag.aliases.bash b/aliases/available/ag.aliases.bash index e3157f94..7f9af7da 100644 --- a/aliases/available/ag.aliases.bash +++ b/aliases/available/ag.aliases.bash @@ -1,4 +1,4 @@ -cite 'about-alias' +# shellcheck shell=bash about-alias 'the silver searcher (ag) aliases' ## Summary for args to less: diff --git a/aliases/available/ansible.aliases.bash b/aliases/available/ansible.aliases.bash index 1c53a88e..04c5d280 100644 --- a/aliases/available/ansible.aliases.bash +++ b/aliases/available/ansible.aliases.bash @@ -1,4 +1,4 @@ -cite 'about-alias' +# shellcheck shell=bash about-alias 'ansible abbreviations' alias ans=ansible diff --git a/aliases/available/apt.aliases.bash b/aliases/available/apt.aliases.bash index 2f444931..1d43ffac 100644 --- a/aliases/available/apt.aliases.bash +++ b/aliases/available/apt.aliases.bash @@ -1,4 +1,4 @@ -#!/bin/bash +# shellcheck shell=bash # # -binaryanomaly @@ -6,9 +6,8 @@ cite 'about-alias' about-alias 'Apt and dpkg aliases for Ubuntu and Debian distros.' # set apt aliases -function _set_pkg_aliases() -{ - if [ -x $(which apt) ]; then +function _set_pkg_aliases() { + if _command_exists apt; then alias apts='apt-cache search' alias aptshow='apt-cache show' alias aptinst='sudo apt-get install -V' diff --git a/aliases/available/atom.aliases.bash b/aliases/available/atom.aliases.bash index 8d70cffa..6868e2cc 100644 --- a/aliases/available/atom.aliases.bash +++ b/aliases/available/atom.aliases.bash @@ -1,4 +1,4 @@ -cite 'about-alias' +# shellcheck shell=bash about-alias 'Atom.io editor abbreviations' alias a='atom' diff --git a/aliases/available/bash-it.aliases.bash b/aliases/available/bash-it.aliases.bash index d2975667..1f16638b 100644 --- a/aliases/available/bash-it.aliases.bash +++ b/aliases/available/bash-it.aliases.bash @@ -1,4 +1,4 @@ -cite about-alias +# shellcheck shell=bash about-alias 'Aliases for the bash-it command (these aliases are automatically included with the "general" aliases)' # Common misspellings of bash-it diff --git a/aliases/available/bolt.aliases.bash b/aliases/available/bolt.aliases.bash index 8490f710..556dd7fe 100644 --- a/aliases/available/bolt.aliases.bash +++ b/aliases/available/bolt.aliases.bash @@ -1,4 +1,4 @@ -cite 'about-alias' +# shellcheck shell=bash about-alias 'puppet bolt aliases' # Aliases diff --git a/aliases/available/bundler.aliases.bash b/aliases/available/bundler.aliases.bash index fc20f4ff..1eb00862 100644 --- a/aliases/available/bundler.aliases.bash +++ b/aliases/available/bundler.aliases.bash @@ -1,4 +1,4 @@ -cite 'about-alias' +# shellcheck shell=bash about-alias 'ruby bundler' # Bundler Commands diff --git a/aliases/available/clipboard.aliases.bash b/aliases/available/clipboard.aliases.bash index 4c7e6f5b..2a5c3e8c 100644 --- a/aliases/available/clipboard.aliases.bash +++ b/aliases/available/clipboard.aliases.bash @@ -1,5 +1,4 @@ # shellcheck shell=bash -cite 'about-alias' about-alias 'xclip shortcuts' alias pbcopy="xclip -selection clipboard" diff --git a/aliases/available/composer.aliases.bash b/aliases/available/composer.aliases.bash index 5ccb2e24..85401abb 100644 --- a/aliases/available/composer.aliases.bash +++ b/aliases/available/composer.aliases.bash @@ -1,4 +1,4 @@ -cite 'about-alias' +# shellcheck shell=bash about-alias 'common composer abbreviations' # Aliases diff --git a/aliases/available/curl.aliases.bash b/aliases/available/curl.aliases.bash index 3ced1bb5..a270e416 100644 --- a/aliases/available/curl.aliases.bash +++ b/aliases/available/curl.aliases.bash @@ -1,22 +1,19 @@ -#!/bin/bash - -cite 'about-alias' +# shellcheck shell=bash about-alias 'Curl aliases for convenience.' # set apt aliases -function _set_pkg_aliases() -{ - if [ -x $(which curl) ]; then +function _set_pkg_aliases() { + if _command_exists curl; then # follow redirects - alias cl='curl -L' - # follow redirects, download as original name - alias clo='curl -L -O' - # follow redirects, download as original name, continue - alias cloc='curl -L -C - -O' - # follow redirects, download as original name, continue, retry 5 times - alias clocr='curl -L -C - -O --retry 5' - # follow redirects, fetch banner - alias clb='curl -L -I' + alias cl='curl -L' + # follow redirects, download as original name + alias clo='curl -L -O' + # follow redirects, download as original name, continue + alias cloc='curl -L -C - -O' + # follow redirects, download as original name, continue, retry 5 times + alias clocr='curl -L -C - -O --retry 5' + # follow redirects, fetch banner + alias clb='curl -L -I' # see only response headers from a get request alias clhead='curl -D - -so /dev/null' fi diff --git a/aliases/available/dnf.aliases.bash b/aliases/available/dnf.aliases.bash index 9d9f0267..25007c23 100644 --- a/aliases/available/dnf.aliases.bash +++ b/aliases/available/dnf.aliases.bash @@ -1,5 +1,4 @@ # shellcheck shell=bash -cite 'about-alias' about-alias 'dnf aliases for fedora 22+ distros' alias dnfl="dnf list" # List packages diff --git a/aliases/available/docker-compose.aliases.bash b/aliases/available/docker-compose.aliases.bash index 3583be8f..a2f637c0 100644 --- a/aliases/available/docker-compose.aliases.bash +++ b/aliases/available/docker-compose.aliases.bash @@ -1,4 +1,4 @@ -cite 'about-alias' +# shellcheck shell=bash about-alias 'docker-compose abbreviations' alias dco="docker-compose" diff --git a/aliases/available/docker.aliases.bash b/aliases/available/docker.aliases.bash index 9f005aa7..1c49207f 100644 --- a/aliases/available/docker.aliases.bash +++ b/aliases/available/docker.aliases.bash @@ -1,32 +1,32 @@ -cite 'about-alias' +# shellcheck shell=bash about-alias 'docker abbreviations' alias dk='docker' -alias dklc='docker ps -l' # List last Docker container -alias dklcid='docker ps -l -q' # List last Docker container ID -alias dklcip='docker inspect -f "{{.NetworkSettings.IPAddress}}" $(docker ps -l -q)' # Get IP of last Docker container -alias dkps='docker ps' # List running Docker containers -alias dkpsa='docker ps -a' # List all Docker containers -alias dki='docker images' # List Docker images -alias dkrmac='docker rm $(docker ps -a -q)' # Delete all Docker containers +alias dklc='docker ps -l' # List last Docker container +alias dklcid='docker ps -l -q' # List last Docker container ID +alias dklcip='docker inspect -f "{{.NetworkSettings.IPAddress}}" $(docker ps -l -q)' # Get IP of last Docker container +alias dkps='docker ps' # List running Docker containers +alias dkpsa='docker ps -a' # List all Docker containers +alias dki='docker images' # List Docker images +alias dkrmac='docker rm $(docker ps -a -q)' # Delete all Docker containers case $OSTYPE in - darwin*|*bsd*|*BSD*) - alias dkrmui='docker images -q -f dangling=true | xargs docker rmi' # Delete all untagged Docker images - ;; - *) - alias dkrmui='docker images -q -f dangling=true | xargs -r docker rmi' # Delete all untagged Docker images - ;; + darwin* | *bsd* | *BSD*) + alias dkrmui='docker images -q -f dangling=true | xargs docker rmi' # Delete all untagged Docker images + ;; + *) + alias dkrmui='docker images -q -f dangling=true | xargs -r docker rmi' # Delete all untagged Docker images + ;; esac -if [ ! -z "$(command ls "${BASH_IT}/enabled/"{[0-9][0-9][0-9]${BASH_IT_LOAD_PRIORITY_SEPARATOR}docker,docker}.plugin.bash 2>/dev/null | head -1)" ]; then -# Function aliases from docker plugin: - alias dkrmlc='docker-remove-most-recent-container' # Delete most recent (i.e., last) Docker container - alias dkrmall='docker-remove-stale-assets' # Delete all untagged images and exited containers - alias dkrmli='docker-remove-most-recent-image' # Delete most recent (i.e., last) Docker image - alias dkrmi='docker-remove-images' # Delete images for supplied IDs or all if no IDs are passed as arguments - alias dkideps='docker-image-dependencies' # Output a graph of image dependencies using Graphiz - alias dkre='docker-runtime-environment' # List environmental variables of the supplied image ID +if _bash-it-component-item-is-enabled plugin docker; then + # Function aliases from docker plugin: + alias dkrmlc='docker-remove-most-recent-container' # Delete most recent (i.e., last) Docker container + alias dkrmall='docker-remove-stale-assets' # Delete all untagged images and exited containers + alias dkrmli='docker-remove-most-recent-image' # Delete most recent (i.e., last) Docker image + alias dkrmi='docker-remove-images' # Delete images for supplied IDs or all if no IDs are passed as arguments + alias dkideps='docker-image-dependencies' # Output a graph of image dependencies using Graphiz + alias dkre='docker-runtime-environment' # List environmental variables of the supplied image ID fi alias dkelc='docker exec -it $(dklcid) bash --login' # Enter last container (works with Docker 1.3 and above) alias dkrmflast='docker rm -f $(dklcid)' diff --git a/aliases/available/emacs.aliases.bash b/aliases/available/emacs.aliases.bash index f8e1259b..a4e4111a 100644 --- a/aliases/available/emacs.aliases.bash +++ b/aliases/available/emacs.aliases.bash @@ -1,16 +1,16 @@ -cite 'about-alias' +# shellcheck shell=bash about-alias 'emacs editor' case $OSTYPE in - linux*) - alias em='emacs' - alias en='emacs -nw' - alias e='emacsclient -n' - alias et='emacsclient -t' - alias ed='emacs --daemon' - alias E='SUDO_EDITOR=emacsclient sudo -e' - ;; - darwin*) - alias em='open -a emacs' - ;; + linux*) + alias em='emacs' + alias en='emacs -nw' + alias e='emacsclient -n' + alias et='emacsclient -t' + alias ed='emacs --daemon' + alias E='SUDO_EDITOR=emacsclient sudo -e' + ;; + darwin*) + alias em='open -a emacs' + ;; esac diff --git a/aliases/available/fuck.aliases.bash b/aliases/available/fuck.aliases.bash index 495ea851..4cfa52d8 100644 --- a/aliases/available/fuck.aliases.bash +++ b/aliases/available/fuck.aliases.bash @@ -1,9 +1,9 @@ -cite 'about-alias' +# shellcheck shell=bash about-alias 'fuck/please to retry last command with sudo' # Play nicely with 'thefuck' plugin -if ! _command_exists fuck ; then - alias fuck='sudo $(fc -ln -1)' +if ! _command_exists fuck; then + alias fuck='sudo $(fc -ln -1)' fi alias please=fuck alias plz=please diff --git a/aliases/available/general.aliases.bash b/aliases/available/general.aliases.bash index 0c7bcd9d..b4934489 100644 --- a/aliases/available/general.aliases.bash +++ b/aliases/available/general.aliases.bash @@ -1,50 +1,45 @@ -cite about-alias +# shellcheck shell=bash about-alias 'general aliases' -if ls --color -d . &> /dev/null -then - alias ls="ls --color=auto" -elif ls -G -d . &> /dev/null -then - alias ls='ls -G' # Compact view, show colors +if command ls --color -d . &> /dev/null; then + alias ls='ls --color=auto' + # BSD `ls` doesn't need an argument (`-G`) when `$CLICOLOR` is set. fi # List directory contents alias sl=ls -alias la='ls -AF' # Compact view, show hidden +alias la='ls -AF' # Compact view, show hidden alias ll='ls -al' alias l='ls -a' alias l1='ls -1' +alias lf='ls -F' -alias _="sudo" +alias _='sudo' # Shortcuts to edit startup files -alias vbrc="vim ~/.bashrc" -alias vbpf="vim ~/.bash_profile" +alias vbrc='${VISUAL:-vim} ~/.bashrc' +alias vbpf='${VISUAL:-vim} ~/.bash_profile' # colored grep # Need to check an existing file for a pattern that will be found to ensure # that the check works when on an OS that supports the color option -if grep --color=auto "a" "${BASH_IT}/"*.md &> /dev/null -then - alias grep='grep --color=auto' +if command grep --color=auto "a" "${BASH_IT?}"/*.md &> /dev/null; then + alias grep='grep --color=auto' fi -if which gshuf &> /dev/null -then - alias shuf=gshuf +if _command_exists gshuf; then + alias shuf=gshuf fi alias c='clear' -alias k='clear' alias cls='clear' -alias edit="$EDITOR" -alias pager="$PAGER" +alias edit='${EDITOR:-${ALTERNATE_EDITOR:-nano}}' +alias pager='${PAGER:=less}' alias q='exit' -alias irc="${IRC_CLIENT:=irc}" +alias irc='${IRC_CLIENT:=irc}' # Language aliases alias rb='ruby' @@ -65,9 +60,8 @@ alias -- -='cd -' # Go back alias h='history' # Tree -if [ ! -x "$(which tree 2>/dev/null)" ] -then - alias tree="find . -print | sed -e 's;[^/]*/;|____;g;s;____|; |;g'" +if ! _command_exists tree; then + alias tree="find . -print | sed -e 's;[^/]*/;|____;g;s;____|; |;g'" fi # Directory @@ -75,21 +69,21 @@ alias md='mkdir -p' alias rd='rmdir' # Shorten extract -alias xt="extract" +alias xt='extract' # sudo editors -alias svim="sudo vim" -alias snano="sudo nano" +alias svim='sudo ${VISUAL:-vim}' +alias snano='sudo nano' # Display whatever file is regular file or folder -catt() { - for i in "$@"; do - if [ -d "$i" ]; then - ls "$i" - else - cat "$i" - fi - done +function catt() { + for i in "$@"; do + if [[ -d "$i" ]]; then + ls "$i" + else + cat "$i" + fi + done } # The Bash-it aliases were moved to the `bash-it.aliases.bash` file. The intent of this @@ -98,5 +92,5 @@ catt() { # aliases and enable just the ones for Bash-it explicitly: # bash-it disable alias general # bash-it enable alias bash-it -# shellcheck source=./bash-it.aliases.bash +# shellcheck source-path=SCRIPTDIR source "$BASH_IT/aliases/available/bash-it.aliases.bash" diff --git a/aliases/available/git.aliases.bash b/aliases/available/git.aliases.bash index 8cafa82b..507037e1 100644 --- a/aliases/available/git.aliases.bash +++ b/aliases/available/git.aliases.bash @@ -1,5 +1,4 @@ # shellcheck shell=bash -cite 'about-alias' about-alias 'common git abbreviations' alias g='git' @@ -131,6 +130,7 @@ alias grb='git rebase' alias grbc='git rebase --continue' alias grm='git rebase $(get_default_branch)' alias grmi='git rebase $(get_default_branch) -i' +alias grma='GIT_SEQUENCE_EDITOR=: git rebase $(get_default_branch) -i --autosquash' alias gprom='git fetch origin $(get_default_branch) && git rebase origin/$(get_default_branch) && git update-ref refs/heads/$(get_default_branch) origin/$(get_default_branch)' # Rebase with latest remote # reset @@ -199,7 +199,7 @@ function gdv() { } function get_default_branch() { - if git branch | grep -q main; then + if git branch | grep -q '^. main\s*$'; then echo main else echo master diff --git a/aliases/available/gitsvn.aliases.bash b/aliases/available/gitsvn.aliases.bash index feb608be..3c578445 100644 --- a/aliases/available/gitsvn.aliases.bash +++ b/aliases/available/gitsvn.aliases.bash @@ -1,4 +1,4 @@ -cite 'about-alias' +# shellcheck shell=bash about-alias 'common git-svn abbreviations' # Aliases diff --git a/aliases/available/heroku.aliases.bash b/aliases/available/heroku.aliases.bash index a749d424..4c822594 100644 --- a/aliases/available/heroku.aliases.bash +++ b/aliases/available/heroku.aliases.bash @@ -1,4 +1,4 @@ -cite 'about-alias' +# shellcheck shell=bash about-alias 'heroku task abbreviations' # heroku diff --git a/aliases/available/hg.aliases.bash b/aliases/available/hg.aliases.bash index eea819ff..d9101a03 100644 --- a/aliases/available/hg.aliases.bash +++ b/aliases/available/hg.aliases.bash @@ -1,4 +1,4 @@ -cite 'about-alias' +# shellcheck shell=bash about-alias 'mercurial abbreviations' alias hs='hg status' diff --git a/aliases/available/homebrew-cask.aliases.bash b/aliases/available/homebrew-cask.aliases.bash index 57d8161c..43d206d4 100644 --- a/aliases/available/homebrew-cask.aliases.bash +++ b/aliases/available/homebrew-cask.aliases.bash @@ -1,7 +1,5 @@ -# Some aliases for Homebrew Cask - -cite 'about-alias' -about-alias 'homebrew-cask abbreviations' +# shellcheck shell=bash +about-alias 'Some aliases for Homebrew Cask' alias bcin='brew cask install' alias bcrm='brew cask uninstall' diff --git a/aliases/available/homebrew.aliases.bash b/aliases/available/homebrew.aliases.bash index 15907518..f35a38d3 100644 --- a/aliases/available/homebrew.aliases.bash +++ b/aliases/available/homebrew.aliases.bash @@ -1,7 +1,5 @@ -# Some aliases for Homebrew - -cite 'about-alias' -about-alias 'homebrew abbreviations' +# shellcheck shell=bash +about-alias 'Some aliases for Homebrew' alias bup='brew update && brew upgrade' alias bout='brew outdated' diff --git a/aliases/available/homesick.aliases.bash b/aliases/available/homesick.aliases.bash index 548efc3b..00101eed 100644 --- a/aliases/available/homesick.aliases.bash +++ b/aliases/available/homesick.aliases.bash @@ -1,4 +1,4 @@ -cite 'about-alias' +# shellcheck shell=bash about-alias 'homesick aliases' # Aliases @@ -19,6 +19,5 @@ alias sikpsh="homesick push dotfiles" alias sikrc="homesick rc dotfiles" alias sikpth="homesick show_path dotfiles" alias sikst="homesick status dotfiles" -alias siktrk="homesick track $1 dotfiles" alias sikulnk="homesick unlink dotfiles" alias sikv="homesick version" diff --git a/aliases/available/jitsu.aliases.bash b/aliases/available/jitsu.aliases.bash index 91e96849..f056e749 100644 --- a/aliases/available/jitsu.aliases.bash +++ b/aliases/available/jitsu.aliases.bash @@ -1,4 +1,4 @@ -cite 'about-alias' +# shellcheck shell=bash about-alias 'jitsu task abbreviations' # jitsu diff --git a/aliases/available/kubectl.aliases.bash b/aliases/available/kubectl.aliases.bash index 440a9041..aaca4ca2 100644 --- a/aliases/available/kubectl.aliases.bash +++ b/aliases/available/kubectl.aliases.bash @@ -1,25 +1,20 @@ -#!/bin/bash -# -# -binaryanomaly - -cite 'about-alias' +# shellcheck shell=bash about-alias 'kubectl aliases' -function _set_pkg_aliases() -{ - if _command_exists kubectl; then - alias kc='kubectl' - alias kcgp='kubectl get pods' - alias kcgd='kubectl get deployments' - alias kcgn='kubectl get nodes' - alias kcdp='kubectl describe pod' - alias kcdd='kubectl describe deployment' - alias kcdn='kubectl describe node' - alias kcgpan='kubectl get pods --all-namespaces' - alias kcgdan='kubectl get deployments --all-namespaces' - # launches a disposable netshoot pod in the k8s cluster - alias kcnetshoot='kubectl run netshoot-$(date +%s) --rm -i --tty --image nicolaka/netshoot -- /bin/bash' - fi +function _set_pkg_aliases() { + if _command_exists kubectl; then + alias kc='kubectl' + alias kcgp='kubectl get pods' + alias kcgd='kubectl get deployments' + alias kcgn='kubectl get nodes' + alias kcdp='kubectl describe pod' + alias kcdd='kubectl describe deployment' + alias kcdn='kubectl describe node' + alias kcgpan='kubectl get pods --all-namespaces' + alias kcgdan='kubectl get deployments --all-namespaces' + # launches a disposable netshoot pod in the k8s cluster + alias kcnetshoot='kubectl run netshoot-$(date +%s) --rm -i --tty --image nicolaka/netshoot -- /bin/bash' + fi } _set_pkg_aliases diff --git a/aliases/available/laravel.aliases.bash b/aliases/available/laravel.aliases.bash index 75a51a01..50a9749f 100644 --- a/aliases/available/laravel.aliases.bash +++ b/aliases/available/laravel.aliases.bash @@ -1,9 +1,9 @@ -cite 'about-alias' +# shellcheck shell=bash about-alias 'laravel artisan abbreviations' # A list of useful laravel aliases -alias laravel="${HOME}/.composer/vendor/bin/laravel" +alias laravel='${HOME?}/.composer/vendor/bin/laravel' # asset alias a:apub='php artisan asset:publish' diff --git a/aliases/available/maven.aliases.bash b/aliases/available/maven.aliases.bash index f8a44a1c..737826eb 100644 --- a/aliases/available/maven.aliases.bash +++ b/aliases/available/maven.aliases.bash @@ -1,4 +1,4 @@ -cite 'about-alias' +# shellcheck shell=bash about-alias 'maven abbreviations' alias mci='mvn clean install' diff --git a/aliases/available/msys2.aliases.bash b/aliases/available/msys2.aliases.bash index a309a5b2..da41cc82 100644 --- a/aliases/available/msys2.aliases.bash +++ b/aliases/available/msys2.aliases.bash @@ -1,6 +1,4 @@ -#!/bin/bash - -cite 'about-alias' +# shellcheck shell=bash about-alias 'MSYS2 aliases' LS_COMMON="-hG" @@ -9,7 +7,8 @@ LS_COMMON="$LS_COMMON -I NTUSER.DAT\* -I ntuser.dat\*" # alias # setup the main ls alias if we've established common args -test -n "$LS_COMMON" && alias ls="command ls $LS_COMMON" +alias ls='command ls ${LS_COMMON:-}' alias ll="ls -l" alias la="ls -a" alias lal="ll -a" +alias lf="ls -F" diff --git a/aliases/available/node.aliases.bash b/aliases/available/node.aliases.bash index a1408f26..a9e29743 100644 --- a/aliases/available/node.aliases.bash +++ b/aliases/available/node.aliases.bash @@ -1,4 +1,4 @@ -cite 'about-alias' +# shellcheck shell=bash about-alias 'the Node.js environment aliases' # alias to setup nodejs development environment diff --git a/aliases/available/npm.aliases.bash b/aliases/available/npm.aliases.bash index bd742d5d..27cf5c9f 100644 --- a/aliases/available/npm.aliases.bash +++ b/aliases/available/npm.aliases.bash @@ -1,4 +1,4 @@ -cite 'about-alias' +# shellcheck shell=bash about-alias 'common npm abbreviations' # Aliases diff --git a/aliases/available/osx.aliases.bash b/aliases/available/osx.aliases.bash index 5e30bc7c..0a16c06f 100644 --- a/aliases/available/osx.aliases.bash +++ b/aliases/available/osx.aliases.bash @@ -1,27 +1,28 @@ -cite 'about-alias' +# shellcheck shell=bash about-alias 'osx-specific aliases' # Desktop Programs -alias fireworks="open -a '/Applications/Adobe Fireworks CS3/Adobe Fireworks CS3.app'" -alias photoshop="open -a '/Applications/Adobe Photoshop CS3/Adobe Photoshop.app'" -alias preview="open -a '$PREVIEW'" -alias xcode="open -a '/Applications/XCode.app'" -alias filemerge="open -a '/Developer/Applications/Utilities/FileMerge.app'" -alias safari="open -a safari" -alias firefox="open -a firefox" -alias chrome="open -a google\ chrome" -alias chromium="open -a chromium" -alias dashcode="open -a dashcode" +alias fireworks='open -a "/Applications/Adobe Fireworks CS3/Adobe Fireworks CS3.app"' +alias photoshop='open -a "/Applications/Adobe Photoshop CS3/Adobe Photoshop.app"' +alias preview='open -a "${PREVIEW?}"' +alias xcode='open -a "/Applications/XCode.app"' +alias filemerge='open -a "/Developer/Applications/Utilities/FileMerge.app"' +alias safari='open -a safari' +alias firefox='open -a firefox' +alias chrome='open -a "Google Chrome"' +alias chromium='open -a chromium' +alias brave='open -a "Brave Browser"' +alias dashcode='open -a dashcode' alias f='open -a Finder ' alias fh='open -a Finder .' alias textedit='open -a TextEdit' alias hex='open -a "Hex Fiend"' alias skype='open -a Skype' alias mou='open -a Mou' -alias subl='open -a Sublime\ Text' +alias subl='open -a "Sublime Text"' -if [ -s /usr/bin/firefox ] ; then - unalias firefox +if [[ -s /usr/bin/firefox ]]; then + unalias firefox fi # Requires growlnotify, which can be found in the Growl DMG under "Extras" @@ -37,19 +38,20 @@ alias whotunes='lsof -r 2 -n -P -F n -c iTunes -a -i TCP@`hostname`:3689' alias flush='dscacheutil -flushcache' # Show/hide hidden files (for Mac OS X Mavericks) -alias showhidden="defaults write com.apple.finder AppleShowAllFiles TRUE" -alias hidehidden="defaults write com.apple.finder AppleShowAllFiles FALSE" +alias showhidden='defaults write com.apple.finder AppleShowAllFiles TRUE' +alias hidehidden='defaults write com.apple.finder AppleShowAllFiles FALSE' # From http://apple.stackexchange.com/questions/110343/copy-last-command-in-terminal -alias copyLastCmd='fc -ln -1 | awk '\''{$1=$1}1'\'' ORS='\'''\'' | pbcopy' +# shellcheck disable=SC2142 # The quoting confuses `shellcheck`... +alias copyLastCmd="fc -ln -1 | awk '{\$1=\$1}1' ORS='' | pbcopy" # Use Finder's Quick Look on a file (^C or space to close) alias ql='qlmanage -p 2>/dev/null' # Mute/Unmute the system volume. Plays nice with all other volume settings. -alias mute="osascript -e 'set volume output muted true'" -alias unmute="osascript -e 'set volume output muted false'" +alias mute='osascript -e "set volume output muted true"' +alias unmute='osascript -e "set volume output muted false"' # Pin to the tail of long commands for an audible alert after long processes ## curl http://downloads.com/hugefile.zip; lmk -alias lmk="say 'Process complete.'" +alias lmk='say "Process complete."' diff --git a/aliases/available/phoenix.aliases.bash b/aliases/available/phoenix.aliases.bash index 64728a2e..08cef4f4 100644 --- a/aliases/available/phoenix.aliases.bash +++ b/aliases/available/phoenix.aliases.bash @@ -1,4 +1,4 @@ -cite 'about-alias' +# shellcheck shell=bash about-alias 'phoenix abbreviations' # Phoenix Commands diff --git a/aliases/available/puppet.aliases.bash b/aliases/available/puppet.aliases.bash index 15b69923..c92d13b1 100644 --- a/aliases/available/puppet.aliases.bash +++ b/aliases/available/puppet.aliases.bash @@ -1,4 +1,4 @@ -cite 'about-alias' +# shellcheck shell=bash about-alias 'puppet aliases' # Aliases diff --git a/aliases/available/pyrocms.aliases.bash b/aliases/available/pyrocms.aliases.bash index d19dff91..77865a23 100644 --- a/aliases/available/pyrocms.aliases.bash +++ b/aliases/available/pyrocms.aliases.bash @@ -1,4 +1,4 @@ -cite 'about-alias' +# shellcheck shell=bash about-alias 'pyrocms abbreviations' ### @@ -7,146 +7,146 @@ about-alias 'pyrocms abbreviations' ### # general -alias a:cl="php artisan clear-compiled" # Remove the compiled class file -alias a:d="php artisan down" # Put the application into maintenance mode -alias a:e="php artisan env" # Display the current framework environment -alias a:h="php artisan help" # Displays help for a command -alias a:i="php artisan install" # Install the Streams Platform. -alias a:ls="php artisan list" # Lists commands -alias a:mg="php artisan migrate" # Run the database migrations -alias a:op="php artisan optimize" # Optimize the framework for better performance (deprecated) -alias a:pr="php artisan preset" # Swap the front-end scaffolding for the application -alias a:s="php artisan serve" # Serve the application on the PHP development server -alias a:u="php artisan up" # Bring the application out of maintenance mode +alias a:cl="php artisan clear-compiled" # Remove the compiled class file +alias a:d="php artisan down" # Put the application into maintenance mode +alias a:e="php artisan env" # Display the current framework environment +alias a:h="php artisan help" # Displays help for a command +alias a:i="php artisan install" # Install the Streams Platform. +alias a:ls="php artisan list" # Lists commands +alias a:mg="php artisan migrate" # Run the database migrations +alias a:op="php artisan optimize" # Optimize the framework for better performance (deprecated) +alias a:pr="php artisan preset" # Swap the front-end scaffolding for the application +alias a:s="php artisan serve" # Serve the application on the PHP development server +alias a:u="php artisan up" # Bring the application out of maintenance mode # addon -alias a:ad:i="php artisan addon:install" # Install an addon. -alias a:ad:p="php artisan addon:publish" # Publish an the configuration and translations for an addon. -alias a:ad:r="php artisan addon:reinstall" # Reinstall an addon. -alias a:ad:u="php artisan addon:uninstall" # Uninstall an addon. +alias a:ad:i="php artisan addon:install" # Install an addon. +alias a:ad:p="php artisan addon:publish" # Publish an the configuration and translations for an addon. +alias a:ad:r="php artisan addon:reinstall" # Reinstall an addon. +alias a:ad:u="php artisan addon:uninstall" # Uninstall an addon. # app -alias a:ap:n="php artisan app:name" # Set the application namespace -alias a:ap:p="php artisan app:publish" # Publish general application override files. +alias a:ap:n="php artisan app:name" # Set the application namespace +alias a:ap:p="php artisan app:publish" # Publish general application override files. # assets -alias a:as:cl="php artisan assets:clear" # Clear compiled public assets. +alias a:as:cl="php artisan assets:clear" # Clear compiled public assets. # auth -alias a:au:clrs="php artisan auth:clear-resets" # Flush expired password reset tokens +alias a:au:clrs="php artisan auth:clear-resets" # Flush expired password reset tokens # cache -alias a:ca:cl="php artisan cache:clear" # Flush the application cache -alias a:ca:f="php artisan cache:forget" # Remove an item from the cache -alias a:ca:t="php artisan cache:table" # Create a migration for the cache database table +alias a:ca:cl="php artisan cache:clear" # Flush the application cache +alias a:ca:f="php artisan cache:forget" # Remove an item from the cache +alias a:ca:t="php artisan cache:table" # Create a migration for the cache database table # config -alias a:co:ca="php artisan config:cache" # Create a cache file for faster configuration loading -alias a:co:cl="php artisan config:clear" # Remove the configuration cache file +alias a:co:ca="php artisan config:cache" # Create a cache file for faster configuration loading +alias a:co:cl="php artisan config:clear" # Remove the configuration cache file # db -alias a:db:s="php artisan db:seed" # Seed the database with records +alias a:db:s="php artisan db:seed" # Seed the database with records # env -alias a:en:s="php artisan env:set" # Set an environmental value. +alias a:en:s="php artisan env:set" # Set an environmental value. # event -alias a:ev:g="php artisan event:generate" # Generate the missing events and listeners based on registration +alias a:ev:g="php artisan event:generate" # Generate the missing events and listeners based on registration # extension -alias a:ex:i="php artisan extension:install" # Install a extension. -alias a:ex:r="php artisan extension:reinstall" # Reinstall a extension. -alias a:ex:u="php artisan extension:uninstall" # Uninstall a extension. +alias a:ex:i="php artisan extension:install" # Install a extension. +alias a:ex:r="php artisan extension:reinstall" # Reinstall a extension. +alias a:ex:u="php artisan extension:uninstall" # Uninstall a extension. # files -alias a:fi:cl="php artisan files:clean" # Clean missing files from the files table. +alias a:fi:cl="php artisan files:clean" # Clean missing files from the files table. # key -alias a:ke:g="php artisan key:generate" # Set the application key +alias a:ke:g="php artisan key:generate" # Set the application key # make -alias a:mk:ad="php artisan make:addon" # Create a new addon. -alias a:mk:au="php artisan make:auth" # Scaffold basic login and registration views and routes -alias a:mk:cm="php artisan make:command" # Create a new Artisan command -alias a:mk:ct="php artisan make:controller" # Create a new controller class -alias a:mk:ev="php artisan make:event" # Create a new event class -alias a:mk:fa="php artisan make:factory" # Create a new model factory -alias a:mk:j="php artisan make:job" # Create a new job class -alias a:mk:li="php artisan make:listener" # Create a new event listener class -alias a:mk:ma="php artisan make:mail" # Create a new email class -alias a:mk:mw="php artisan make:middleware" # Create a new middleware class -alias a:mk:mg="php artisan make:migration" # Create a new migration file -alias a:mk:md="php artisan make:model" # Create a new Eloquent model class -alias a:mk:no="php artisan make:notification" # Create a new notification class -alias a:mk:po="php artisan make:policy" # Create a new policy class -alias a:mk:pr="php artisan make:provider" # Create a new service provider class -alias a:mk:rq="php artisan make:request" # Create a new form request class -alias a:mk:rs="php artisan make:resource" # Create a new resource -alias a:mk:rl="php artisan make:rule" # Create a new validation rule -alias a:mk:sd="php artisan make:seeder" # Create a new seeder class -alias a:mk:st="php artisan make:stream" # Make a streams entity namespace. -alias a:mk:ts="php artisan make:test" # Create a new test class +alias a:mk:ad="php artisan make:addon" # Create a new addon. +alias a:mk:au="php artisan make:auth" # Scaffold basic login and registration views and routes +alias a:mk:cm="php artisan make:command" # Create a new Artisan command +alias a:mk:ct="php artisan make:controller" # Create a new controller class +alias a:mk:ev="php artisan make:event" # Create a new event class +alias a:mk:fa="php artisan make:factory" # Create a new model factory +alias a:mk:j="php artisan make:job" # Create a new job class +alias a:mk:li="php artisan make:listener" # Create a new event listener class +alias a:mk:ma="php artisan make:mail" # Create a new email class +alias a:mk:mw="php artisan make:middleware" # Create a new middleware class +alias a:mk:mg="php artisan make:migration" # Create a new migration file +alias a:mk:md="php artisan make:model" # Create a new Eloquent model class +alias a:mk:no="php artisan make:notification" # Create a new notification class +alias a:mk:po="php artisan make:policy" # Create a new policy class +alias a:mk:pr="php artisan make:provider" # Create a new service provider class +alias a:mk:rq="php artisan make:request" # Create a new form request class +alias a:mk:rs="php artisan make:resource" # Create a new resource +alias a:mk:rl="php artisan make:rule" # Create a new validation rule +alias a:mk:sd="php artisan make:seeder" # Create a new seeder class +alias a:mk:st="php artisan make:stream" # Make a streams entity namespace. +alias a:mk:ts="php artisan make:test" # Create a new test class # migrate -alias a:mg:fr="php artisan migrate:fresh" # Drop all tables and re-run all migrations -alias a:mg:i="php artisan migrate:install" # Create the migration repository -alias a:mg:rf="php artisan migrate:refresh" # Reset and re-run all migrations -alias a:mg:rs="php artisan migrate:reset" # Rollback all database migrations -alias a:mg:rl="php artisan migrate:rollback" # Rollback the last database migration -alias a:mg:st="php artisan migrate:status" # Show the status of each migration +alias a:mg:fr="php artisan migrate:fresh" # Drop all tables and re-run all migrations +alias a:mg:i="php artisan migrate:install" # Create the migration repository +alias a:mg:rf="php artisan migrate:refresh" # Reset and re-run all migrations +alias a:mg:rs="php artisan migrate:reset" # Rollback all database migrations +alias a:mg:rl="php artisan migrate:rollback" # Rollback the last database migration +alias a:mg:st="php artisan migrate:status" # Show the status of each migration # module -alias a:mo:i="php artisan module:install" # Install a module. -alias a:mo:r="php artisan module:reinstall" # Reinstall a module. -alias a:mo:u="php artisan module:uninstall" # Uninstall a module. +alias a:mo:i="php artisan module:install" # Install a module. +alias a:mo:r="php artisan module:reinstall" # Reinstall a module. +alias a:mo:u="php artisan module:uninstall" # Uninstall a module. # notifications -alias a:no:tb="php artisan notifications:table" # Create a migration for the notifications table +alias a:no:tb="php artisan notifications:table" # Create a migration for the notifications table # package -alias a:pk:d="php artisan package:discover" # Rebuild the cached package manifest +alias a:pk:d="php artisan package:discover" # Rebuild the cached package manifest # queue -alias a:qu:fa="php artisan queue:failed" # List all of the failed queue jobs -alias a:qu:ft="php artisan queue:failed-table" # Create a migration for the failed queue jobs database table -alias a:qu:fl="php artisan queue:flush" # Flush all of the failed queue jobs -alias a:qu:fg="php artisan queue:forget" # Delete a failed queue job -alias a:qu:li="php artisan queue:listen" # Listen to a given queue -alias a:qu:rs="php artisan queue:restart" # Restart queue worker daemons after their current job -alias a:qu:rt="php artisan queue:retry" # Retry a failed queue job -alias a:qu:tb="php artisan queue:table" # Create a migration for the queue jobs database table -alias a:qu:w="php artisan queue:work" # Start processing jobs on the queue as a daemon +alias a:qu:fa="php artisan queue:failed" # List all of the failed queue jobs +alias a:qu:ft="php artisan queue:failed-table" # Create a migration for the failed queue jobs database table +alias a:qu:fl="php artisan queue:flush" # Flush all of the failed queue jobs +alias a:qu:fg="php artisan queue:forget" # Delete a failed queue job +alias a:qu:li="php artisan queue:listen" # Listen to a given queue +alias a:qu:rs="php artisan queue:restart" # Restart queue worker daemons after their current job +alias a:qu:rt="php artisan queue:retry" # Retry a failed queue job +alias a:qu:tb="php artisan queue:table" # Create a migration for the queue jobs database table +alias a:qu:w="php artisan queue:work" # Start processing jobs on the queue as a daemon # route -alias a:ro:ca="php artisan route:cache" # Create a route cache file for faster route registration -alias a:ro:cl="php artisan route:clear" # Remove the route cache file -alias a:ro:ls="php artisan route:list" # List all registered routes +alias a:ro:ca="php artisan route:cache" # Create a route cache file for faster route registration +alias a:ro:cl="php artisan route:clear" # Remove the route cache file +alias a:ro:ls="php artisan route:list" # List all registered routes # schedule -alias a:sc:r="php artisan schedule:run" # Run the scheduled commands +alias a:sc:r="php artisan schedule:run" # Run the scheduled commands # scout -alias a:su:fl="php artisan scout:flush" # Flush all of the model's records from the index -alias a:su:im="php artisan scout:import" # Import the given model into the search index +alias a:su:fl="php artisan scout:flush" # Flush all of the model's records from the index +alias a:su:im="php artisan scout:import" # Import the given model into the search index # session -alias a:se:tb="php artisan session:table" # Create a migration for the session database table +alias a:se:tb="php artisan session:table" # Create a migration for the session database table # storage -alias a:sg:l="php artisan storage:link" # Create a symbolic link from "public/storage" to "storage/app/public" +alias a:sg:l="php artisan storage:link" # Create a symbolic link from "public/storage" to "storage/app/public" # streams -alias a:st:cl="php artisan streams:cleanup" # Cleanup streams entry models. -alias a:st:co="php artisan streams:compile" # Compile streams entry models. -alias a:st:d="php artisan streams:destroy" # Destroy a namespace. -alias a:st:p="php artisan streams:publish" # Publish configuration and translations for streams. -alias a:st:r="php artisan streams:refresh" # Refresh streams generated components. +alias a:st:cl="php artisan streams:cleanup" # Cleanup streams entry models. +alias a:st:co="php artisan streams:compile" # Compile streams entry models. +alias a:st:d="php artisan streams:destroy" # Destroy a namespace. +alias a:st:p="php artisan streams:publish" # Publish configuration and translations for streams. +alias a:st:r="php artisan streams:refresh" # Refresh streams generated components. # tntsearch -alias a:tn:im="php artisan tntsearch:import" # Import the given model into the search index +alias a:tn:im="php artisan tntsearch:import" # Import the given model into the search index # vendor -alias a:ve:p="php artisan vendor:publish" # Publish any publishable assets from vendor packages +alias a:ve:p="php artisan vendor:publish" # Publish any publishable assets from vendor packages # view -alias a:vi:cl="php artisan view:clear" # Clear all compiled view files +alias a:vi:cl="php artisan view:clear" # Clear all compiled view files diff --git a/aliases/available/rails.aliases.bash b/aliases/available/rails.aliases.bash index c776660e..4de4faff 100644 --- a/aliases/available/rails.aliases.bash +++ b/aliases/available/rails.aliases.bash @@ -1,4 +1,4 @@ -cite 'about-alias' +# shellcheck shell=bash about-alias 'rails abbreviations' # Rails Commands @@ -14,9 +14,9 @@ alias rd='rails destroy' alias dbm='rake db:migrate' alias ss='script/server' -alias ts="thin start" # thin server +alias ts="thin start" # thin server alias sc='script/console' alias restartapp='touch tmp/restart.txt' -alias restart='touch tmp/restart.txt' # restart passenger +alias restart='touch tmp/restart.txt' # restart passenger alias devlog='tail -f log/development.log' alias taild='tail -f log/development.log' # tail dev log diff --git a/aliases/available/svn.aliases.bash b/aliases/available/svn.aliases.bash index 3d6d263e..4d3de464 100644 --- a/aliases/available/svn.aliases.bash +++ b/aliases/available/svn.aliases.bash @@ -1,4 +1,4 @@ -cite 'about-alias' +# shellcheck shell=bash about-alias 'common svn abbreviations' # Aliases diff --git a/aliases/available/systemd.aliases.bash b/aliases/available/systemd.aliases.bash index 19b0eae6..57351ae0 100644 --- a/aliases/available/systemd.aliases.bash +++ b/aliases/available/systemd.aliases.bash @@ -1,22 +1,22 @@ -cite 'about-alias' +# shellcheck shell=bash about-alias 'systemd service' case $OSTYPE in - linux*) -# Improve aliases by bringing the common root `sc|scd` + `sre` for action + `u` for user - alias sc='systemctl' - alias scu='systemctl --user' - alias scdr='systemctl daemon-reload' - alias scdru='systemctl --user daemon-reload' - alias scr='systemctl restart' - alias scru='systemctl --user restart' - alias sce='systemctl stop' - alias sceu='systemctl --user stop' - alias scs='systemctl start' - alias scsu='systemctl --user start' -# Keeping previous aliases for a non-breaking change. - alias scue='sceu' - alias scus='scsu' - alias scur='scdru' - ;; + linux*) + # Improve aliases by bringing the common root `sc|scd` + `sre` for action + `u` for user + alias sc='systemctl' + alias scu='systemctl --user' + alias scdr='systemctl daemon-reload' + alias scdru='systemctl --user daemon-reload' + alias scr='systemctl restart' + alias scru='systemctl --user restart' + alias sce='systemctl stop' + alias sceu='systemctl --user stop' + alias scs='systemctl start' + alias scsu='systemctl --user start' + # Keeping previous aliases for a non-breaking change. + alias scue='sceu' + alias scus='scsu' + alias scur='scdru' + ;; esac diff --git a/aliases/available/terraform.aliases.bash b/aliases/available/terraform.aliases.bash index 09380868..baa9b0c7 100644 --- a/aliases/available/terraform.aliases.bash +++ b/aliases/available/terraform.aliases.bash @@ -1,7 +1,5 @@ -# Aliases for Terraform and Terragrunt - -cite 'about-alias' -about-alias 'Terraform abbreviations' +# shellcheck shell=bash +about-alias 'Aliases for Terraform and Terragrunt' alias tf='terraform' alias tfv='terraform validate' diff --git a/aliases/available/terragrunt.aliases.bash b/aliases/available/terragrunt.aliases.bash index 9395b351..94892901 100644 --- a/aliases/available/terragrunt.aliases.bash +++ b/aliases/available/terragrunt.aliases.bash @@ -1,7 +1,5 @@ -# Aliases for Terraform and Terragrunt - -cite 'about-alias' -about-alias 'Terragrunt abbreviations' +# shellcheck shell=bash +about-alias 'Aliases for Terraform and Terragrunt' alias tg='terragrunt' alias tgv='terragrunt validate' diff --git a/aliases/available/textmate.aliases.bash b/aliases/available/textmate.aliases.bash index f0f69e43..e53eed1a 100644 --- a/aliases/available/textmate.aliases.bash +++ b/aliases/available/textmate.aliases.bash @@ -1,10 +1,10 @@ -cite 'about-alias' +# shellcheck shell=bash about-alias 'textmate abbreviations' case $OSTYPE in - darwin*) - # Textmate - alias e='mate . &' - alias et='mate app config db lib public script test spec config.ru Gemfile Rakefile README &' - ;; + darwin*) + # Textmate + alias e='mate . &' + alias et='mate app config db lib public script test spec config.ru Gemfile Rakefile README &' + ;; esac diff --git a/aliases/available/tmux.aliases.bash b/aliases/available/tmux.aliases.bash index 1b07f149..192db5b5 100644 --- a/aliases/available/tmux.aliases.bash +++ b/aliases/available/tmux.aliases.bash @@ -1,4 +1,4 @@ -cite 'about-alias' +# shellcheck shell=bash about-alias 'Tmux terminal multiplexer' alias txl='tmux ls' diff --git a/aliases/available/todo.txt-cli.aliases.bash b/aliases/available/todo.txt-cli.aliases.bash index 5bf35d0d..359321a4 100644 --- a/aliases/available/todo.txt-cli.aliases.bash +++ b/aliases/available/todo.txt-cli.aliases.bash @@ -1,8 +1,8 @@ -cite 'about-alias' +# shellcheck shell=bash about-alias 'todo.txt-cli abbreviations' -alias tls="$TODO ls" -alias ta="$TODO a" -alias trm="$TODO rm" -alias tdo="$TODO do" -alias tpri="$TODO pri" +alias tls='"${TODO?}" ls' +alias ta='"${TODO?}" a' +alias trm='"${TODO?}" rm' +alias tdo='"${TODO?}" do' +alias tpri='"${TODO?}" pri' diff --git a/aliases/available/uuidgen.aliases.bash b/aliases/available/uuidgen.aliases.bash index aada05fb..45c36820 100644 --- a/aliases/available/uuidgen.aliases.bash +++ b/aliases/available/uuidgen.aliases.bash @@ -1,11 +1,11 @@ -cite 'uuid-alias' +# shellcheck shell=bash about-alias 'uuidgen aliases' if _command_exists uuid; then # Linux - alias uuidu="uuid | tr '[:lower:]' '[:upper:]'" - alias uuidl=uuid + alias uuidu="uuid | tr '[:lower:]' '[:upper:]'" + alias uuidl=uuid elif _command_exists uuidgen; then # macOS/BSD - alias uuidu="uuidgen" - alias uuid="uuidgen | tr '[:upper:]' '[:lower:]'" # because upper case is like YELLING - alias uuidl=uuid + alias uuidu="uuidgen" + alias uuid="uuidgen | tr '[:upper:]' '[:lower:]'" # because upper case is like YELLING + alias uuidl=uuid fi diff --git a/aliases/available/vagrant.aliases.bash b/aliases/available/vagrant.aliases.bash index d479fb2b..a949cbb3 100644 --- a/aliases/available/vagrant.aliases.bash +++ b/aliases/available/vagrant.aliases.bash @@ -1,4 +1,4 @@ -cite 'about-alias' +# shellcheck shell=bash about-alias 'vagrant aliases' # Aliases diff --git a/aliases/available/vault.aliases.bash b/aliases/available/vault.aliases.bash index d2ad8e74..4d083fb6 100644 --- a/aliases/available/vault.aliases.bash +++ b/aliases/available/vault.aliases.bash @@ -1,4 +1,4 @@ -cite 'about-alias' +# shellcheck shell=bash about-alias 'vault aliases' # Aliases diff --git a/aliases/available/vim.aliases.bash b/aliases/available/vim.aliases.bash index d19057d0..f8068764 100644 --- a/aliases/available/vim.aliases.bash +++ b/aliases/available/vim.aliases.bash @@ -1,25 +1,14 @@ # shellcheck shell=bash -cite 'about-alias' about-alias 'vim abbreviations' -VIM=$(command -v vim) -GVIM=$(command -v gvim) -MVIM=$(command -v mvim) +_command_exists vim || return -if [[ -n $VIM ]]; then - alias v='$VIM' - # open the vim help in fullscreen incorporated from - # https://stackoverflow.com/a/4687513 - alias vimh='${VIM} -c ":h | only"' -fi +alias v='vim' +# open the vim help in fullscreen incorporated from +# https://stackoverflow.com/a/4687513 +alias vimh='vim -c ":h | only"' # open vim in new tab is taken from # http://stackoverflow.com/questions/936501/let-gvim-always-run-a-single-instancek -case $OSTYPE in - darwin*) - [[ -n $MVIM ]] && function mvimt { command mvim --remote-tab-silent "$@" || command mvim "$@"; } - ;; - *) - [[ -n $GVIM ]] && function gvimt { command gvim --remote-tab-silent "$@" || command gvim "$@"; } - ;; -esac +_command_exists mvim && function mvimt { command mvim --remote-tab-silent "$@" || command mvim "$@"; } +_command_exists gvim && function gvimt { command gvim --remote-tab-silent "$@" || command gvim "$@"; } diff --git a/aliases/available/yarn.aliases.bash b/aliases/available/yarn.aliases.bash index b50535b9..a2fb6d0d 100644 --- a/aliases/available/yarn.aliases.bash +++ b/aliases/available/yarn.aliases.bash @@ -1,4 +1,4 @@ -cite 'about-alias' +# shellcheck shell=bash about-alias 'yarn package manager aliases' # Aliases diff --git a/bash_it.sh b/bash_it.sh index b8d3aeef..ddc02b70 100755 --- a/bash_it.sh +++ b/bash_it.sh @@ -1,159 +1,110 @@ #!/usr/bin/env bash +# shellcheck source-path=SCRIPTDIR/lib source-path=SCRIPTDIR/scripts +# shellcheck disable=SC2034 +# # Initialize Bash It BASH_IT_LOG_PREFIX="core: main: " - -# Only set $BASH_IT if it's not already set -if [ -z "${BASH_IT:-}" ]; then - # Setting $BASH to maintain backwards compatibility - export BASH_IT=$BASH - BASH="$(bash -c 'echo $BASH')" - export BASH - BASH_IT_OLD_BASH_SETUP=true -fi +: "${BASH_IT:=${BASH_SOURCE%/*}}" +: "${BASH_IT_CUSTOM:=${BASH_IT}/custom}" +: "${CUSTOM_THEME_DIR:="${BASH_IT_CUSTOM}/themes"}" +: "${BASH_IT_BASHRC:=${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}}" # Load composure first, so we support function metadata -# shellcheck disable=SC1090 -source "${BASH_IT}"/vendor/github.com/erichs/composure/composure.sh - -# We need to load logging module first as well in order to be able to log -# shellcheck source=./lib/log.bash -source "${BASH_IT}/lib/log.bash" - -# We can only log it now -[ -z "${BASH_IT_OLD_BASH_SETUP:-}" ] || _log_warning "BASH_IT variable not initialized, please upgrade your bash-it version and reinstall it!" - -# For backwards compatibility, look in old BASH_THEME location -if [ -z "${BASH_IT_THEME:-}" ]; then - _log_warning "BASH_IT_THEME variable not initialized, please upgrade your bash-it version and reinstall it!" - export BASH_IT_THEME="${BASH_THEME:-}" - unset BASH_THEME -fi - +# shellcheck source-path=SCRIPTDIR/vendor/github.com/erichs/composure +source "${BASH_IT}/vendor/github.com/erichs/composure/composure.sh" # support 'plumbing' metadata cite _about _param _example _group _author _version cite about-alias about-plugin about-completion +# Declare our end-of-main finishing hook, but don't use `declare`/`typeset` +_bash_it_library_finalize_hook=() + +# We need to load logging module early in order to be able to log +source "${BASH_IT}/lib/log.bash" + # libraries, but skip appearance (themes) for now _log_debug "Loading libraries(except appearance)..." -LIB="${BASH_IT}/lib/*.bash" APPEARANCE_LIB="${BASH_IT}/lib/appearance.bash" -for _bash_it_config_file in $LIB; do - if [ "$_bash_it_config_file" != "$APPEARANCE_LIB" ]; then - filename=${_bash_it_config_file##*/} - filename=${filename%.bash} - BASH_IT_LOG_PREFIX="lib: ${filename}: " - _log_debug "Loading library file..." - # shellcheck disable=SC1090 - source "$_bash_it_config_file" - 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)\"..." +for _bash_it_main_file_lib in "${BASH_IT}/lib"/*.bash; do + [[ "$_bash_it_main_file_lib" == "$APPEARANCE_LIB" ]] && continue + _bash-it-log-prefix-by-path "${_bash_it_main_file_lib}" + _log_debug "Loading library file..." # shellcheck disable=SC1090 - source "${_bash_it_vendor_init}" + source "$_bash_it_main_file_lib" + BASH_IT_LOG_PREFIX="core: main: " 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 -source "${BASH_IT}/scripts/reloader.bash" - -# Load enabled aliases, completion, plugins -for file_type in "aliases" "plugins" "completion"; do - # shellcheck source=./scripts/reloader.bash - source "${BASH_IT}/scripts/reloader.bash" "skip" "$file_type" +# Load the global "enabled" directory, then enabled aliases, completion, plugins +# "_bash_it_main_file_type" param is empty so that files get sourced in glob order +for _bash_it_main_file_type in "" "aliases" "plugins" "completion"; do + BASH_IT_LOG_PREFIX="core: reloader: " + # shellcheck disable=SC2140 + source "${BASH_IT}/scripts/reloader.bash" ${_bash_it_main_file_type:+"skip" "$_bash_it_main_file_type"} + BASH_IT_LOG_PREFIX="core: main: " done # Load theme, if a theme was set -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: " - # shellcheck source=./themes/colors.theme.bash - source "${BASH_IT}/themes/colors.theme.bash" +# shellcheck source-path=SCRIPTDIR/themes +if [[ -n "${BASH_IT_THEME:-}" ]]; then + _log_debug "Loading theme '${BASH_IT_THEME}'." BASH_IT_LOG_PREFIX="themes: githelpers: " - # shellcheck source=./themes/githelpers.theme.bash source "${BASH_IT}/themes/githelpers.theme.bash" BASH_IT_LOG_PREFIX="themes: p4helpers: " - # shellcheck source=./themes/p4helpers.theme.bash source "${BASH_IT}/themes/p4helpers.theme.bash" - BASH_IT_LOG_PREFIX="themes: command_duration: " - # shellcheck source=./themes/command_duration.theme.bash - source "${BASH_IT}/themes/command_duration.theme.bash" BASH_IT_LOG_PREFIX="themes: base: " - # shellcheck source=./themes/base.theme.bash source "${BASH_IT}/themes/base.theme.bash" BASH_IT_LOG_PREFIX="lib: appearance: " # appearance (themes) now, after all dependencies - # shellcheck source=./lib/appearance.bash + # shellcheck source=SCRIPTDIR/lib/appearance.bash source "$APPEARANCE_LIB" + BASH_IT_LOG_PREFIX="core: main: " fi -BASH_IT_LOG_PREFIX="core: main: " _log_debug "Loading custom aliases, completion, plugins..." -for file_type in "aliases" "completion" "plugins"; do - if [ -e "${BASH_IT}/${file_type}/custom.${file_type}.bash" ]; then - BASH_IT_LOG_PREFIX="${file_type}: custom: " +for _bash_it_main_file_type in "aliases" "completion" "plugins"; do + _bash_it_main_file_custom="${BASH_IT}/${_bash_it_main_file_type}/custom.${_bash_it_main_file_type}.bash" + if [[ -s "${_bash_it_main_file_custom}" ]]; then + _bash-it-log-prefix-by-path "${_bash_it_main_file_custom}" _log_debug "Loading component..." # shellcheck disable=SC1090 - source "${BASH_IT}/${file_type}/custom.${file_type}.bash" + source "${_bash_it_main_file_custom}" fi + BASH_IT_LOG_PREFIX="core: main: " done # Custom -BASH_IT_LOG_PREFIX="core: main: " _log_debug "Loading general custom files..." -CUSTOM="${BASH_IT_CUSTOM:=${BASH_IT}/custom}/*.bash ${BASH_IT_CUSTOM:=${BASH_IT}/custom}/**/*.bash" -for _bash_it_config_file in $CUSTOM; do - if [ -e "${_bash_it_config_file}" ]; then - filename=$(basename "${_bash_it_config_file}") - filename=${filename%*.bash} - BASH_IT_LOG_PREFIX="custom: $filename: " +for _bash_it_main_file_custom in "${BASH_IT_CUSTOM}"/*.bash "${BASH_IT_CUSTOM}"/*/*.bash; do + if [[ -s "${_bash_it_main_file_custom}" ]]; then + _bash-it-log-prefix-by-path "${_bash_it_main_file_custom}" _log_debug "Loading custom file..." # shellcheck disable=SC1090 - source "$_bash_it_config_file" + source "$_bash_it_main_file_custom" fi + BASH_IT_LOG_PREFIX="core: main: " done -unset _bash_it_config_file -if [[ "${PROMPT:-}" ]]; then - export PS1="\[""$PROMPT""\]" +if [[ -n "${PROMPT:-}" ]]; then + PS1="${PROMPT}" fi # Adding Support for other OSes -PREVIEW="less" - -if [ -s /usr/bin/gloobus-preview ]; then +if _command_exists gloobus-preview; then PREVIEW="gloobus-preview" -elif [ -s /Applications/Preview.app ]; then - # shellcheck disable=SC2034 +elif [[ -d /Applications/Preview.app ]]; then PREVIEW="/Applications/Preview.app" -fi - -# Load all the Jekyll stuff - -if [ -e "$HOME/.jekyllconfig" ]; then - # shellcheck disable=SC1090 - . "$HOME/.jekyllconfig" +else + PREVIEW="less" fi # BASH_IT_RELOAD_LEGACY is set. -if ! command -v reload &> /dev/null && [ -n "${BASH_IT_RELOAD_LEGACY:-}" ]; then - case $OSTYPE in - darwin*) - alias reload='source ~/.bash_profile' - ;; - *) - alias reload='source ~/.bashrc' - ;; - esac +if [[ -n "${BASH_IT_RELOAD_LEGACY:-}" ]] && ! _command_exists reload; then + # shellcheck disable=SC2139 + alias reload="builtin source '${BASH_IT_BASHRC?}'" fi -# Disable trap DEBUG on subshells - https://github.com/Bash-it/bash-it/pull/1040 -set +T +for _bash_it_library_finalize_f in "${_bash_it_library_finalize_hook[@]:-}"; do + eval "${_bash_it_library_finalize_f?}" # Use `eval` to achieve the same behavior as `$PROMPT_COMMAND`. +done +unset "${!_bash_it_library_finalize_@}" "${!_bash_it_main_file_@}" diff --git a/clean_files.txt b/clean_files.txt index b8fcee4f..de0f6c1e 100644 --- a/clean_files.txt +++ b/clean_files.txt @@ -16,8 +16,10 @@ # root directories # +aliases/ docs/ hooks/ +scripts/ # root files # @@ -27,16 +29,12 @@ clean_files.txt install.sh lint_clean_files.sh -# aliases -# -aliases/available/dnf.aliases.bash -aliases/available/git.aliases.bash -aliases/available/vim.aliases.bash - # completions # completion/available/apm.completion.bash completion/available/awless.completion.bash +completion/available/awscli.completion.bash +completion/available/bash-it.completion.bash completion/available/brew.completion.bash completion/available/cargo.completion.bash completion/available/composer.completion.bash @@ -47,8 +45,10 @@ completion/available/django.completion.bash completion/available/dmidecode.completion.bash completion/available/docker-machine.completion.bash completion/available/docker.completion.bash +completion/available/dotnet.completion.bash completion/available/gcloud.completion.bash completion/available/gem.completion.bash +completion/available/git.completion.bash completion/available/github-cli.completion.bash completion/available/go.completion.bash completion/available/helm.completion.bash @@ -58,6 +58,7 @@ completion/available/jungle.completion.bash completion/available/knife.completion.bash completion/available/kontena.completion.bash completion/available/kubectl.completion.bash +completion/available/laravel.completion.bash completion/available/lerna.completion.bash completion/available/minikube.completion.bash completion/available/ngrok.completion.bash @@ -70,27 +71,74 @@ completion/available/pipenv.completion.bash completion/available/pipx.completion.bash completion/available/rustup.completion.bash completion/available/sdkman.completion.bash +completion/available/system.completion.bash completion/available/vault.completion.bash completion/available/vuejs.completion.bash completion/available/wpscan.completion.bash +# libraries +lib/appearance.bash +lib/colors.bash +lib/command_duration.bash +lib/helpers.bash +lib/history.bash +lib/log.bash +lib/preexec.bash +lib/preview.bash +lib/search.bash +lib/utilities.bash + # plugins # plugins/available/alias-completion.plugin.bash +plugins/available/autojump.plugin.bash +plugins/available/base.plugin.bash plugins/available/basher.plugin.bash +plugins/available/battery.plugin.bash +plugins/available/blesh.plugin.bash plugins/available/cmd-returned-notify.plugin.bash +plugins/available/colors.plugin.bash +plugins/available/direnv.plugin.bash +plugins/available/dirs.plugin.bash plugins/available/docker-machine.plugin.bash +plugins/available/gif.plugin.bash +plugins/available/git-subrepo.plugin.bash plugins/available/git.plugin.bash plugins/available/go.plugin.bash plugins/available/goenv.plugin.bash +plugins/available/history-eternal.plugin.bash plugins/available/history-search.plugin.bash plugins/available/history-substring-search.plugin.bash plugins/available/history.plugin.bash +plugins/available/hub.plugin.bash +plugins/available/java.plugin.bash +plugins/available/jekyll.plugin.bash +plugins/available/jump.plugin.bash +plugins/available/latex.plugin.bash +plugins/available/less-pretty-cat.plugin.bash +plugins/available/man.plugin.bash +plugins/available/nginx.plugin.bash +plugins/available/node.plugin.bash +plugins/available/nodenv.plugin.bash +plugins/available/osx-timemachine.plugin.bash +plugins/available/osx.plugin.bash +plugins/available/percol.plugin.bash +plugins/available/plenv.plugin.bash +plugins/available/projects.plugin.bash +plugins/available/proxy.plugin.bash +plugins/available/pyenv.plugin.bash +plugins/available/python.plugin.bash +plugins/available/rbenv.plugin.bash +plugins/available/ruby.plugin.bash +plugins/available/textmate.plugin.bash +plugins/available/todo.plugin.bash plugins/available/xterm.plugin.bash +plugins/available/zoxide.plugin.bash # tests # -test/plugins/alias-completion.plugin.bats +test/completion/aliases.completion.bats +test/run test/test_helper.bash # themes @@ -109,9 +157,14 @@ themes/bobby-python themes/brainy themes/brunton themes/candy -themes/command_duration.theme.bash themes/easy +themes/essential +themes/githelpers.theme.bash themes/modern +themes/norbu +themes/oh-my-posh +themes/p4helpers.theme.bash +themes/pete themes/powerline themes/pure themes/purity @@ -119,4 +172,3 @@ themes/purity # vendor init files # vendor/.gitattributes -vendor/init.d diff --git a/completion/available/aliases.completion.bash b/completion/available/aliases.completion.bash new file mode 100644 index 00000000..f9cc1ed1 --- /dev/null +++ b/completion/available/aliases.completion.bash @@ -0,0 +1,112 @@ +# shellcheck shell=bash +about-plugin 'Automatic completion of aliases' +# Load after all aliases and completions to understand what needs to be completed +# BASH_IT_LOAD_PRIORITY: 800 + +# References: +# http://superuser.com/a/437508/119764 +# http://stackoverflow.com/a/1793178/1228454 + +# Automatically add completion for all aliases to commands having completion functions +function _bash-it-component-completion-callback-on-init-aliases() { + local namespace="alias_completion" + local tmp_file completion_loader alias_name line completions chars + local alias_arg_words new_completion compl_func compl_wrapper alias_defn + + # create array of function completion triggers, keeping multi-word triggers together + IFS=$'\n' read -d '' -ra completions < <(complete -p) + ((${#completions[@]} == 0)) && return 0 + + completions=("${completions[@]##complete -* * -}") # strip all but last option plus trigger(s) + completions=("${completions[@]#complete -}") # strip anything missed + completions=("${completions[@]#? * }") # strip last option and arg, leaving only trigger(s) + completions=("${completions[@]#? }") # strip anything missed + #TODO: this will fail on some completions... + + # create temporary file for wrapper functions and completions + tmp_file="$(mktemp -t "${namespace}-${RANDOM}XXXXXX")" || return 1 + + IFS=$'\n' read -r completion_loader < <(complete -p -D 2> /dev/null) + if [[ "${completion_loader#complete }" =~ '-F'[[:space:]]([[:alnum:]_]+)[[:space:]] ]]; then + completion_loader="${BASH_REMATCH[1]}" + else + completion_loader="" + fi + + # read in " '' ''" lines from defined aliases + # some aliases do have backslashes that needs to be interpreted + # shellcheck disable=SC2162 + while read line; do + line="${line#alias -- }" + line="${line#alias }" + alias_name="${line%%=*}" + alias_defn="${line#*=\'}" # alias definition + alias_defn="${alias_defn%\'}" + alias_cmd="${alias_defn%%[[:space:]]*}" # first word of alias + if [[ ${alias_defn} == ${alias_cmd} ]]; then + alias_args='' + else + alias_args="${alias_defn#*[[:space:]]}" # everything after first word + fi + + # skip aliases to pipes, boolean control structures and other command lists + chars='\|\&\;\)\(\n\<\>' + if [[ "${alias_defn}" =~ [$chars] ]]; then + continue + fi + # avoid expanding wildcards + read -ra alias_arg_words <<< "$alias_args" + + # skip alias if there is no completion function triggered by the aliased command + if ! _bash-it-array-contains-element "$alias_cmd" "${completions[@]}"; then + if [[ -n "$completion_loader" ]]; then + # force loading of completions for the aliased command + "${completion_loader:?}" "${alias_cmd}" + # 124 means completion loader was successful + [[ $? -eq 124 ]] || continue + completions+=("$alias_cmd") + else + continue + fi + fi + new_completion="$(complete -p "$alias_cmd" 2> /dev/null)" + + # create a wrapper inserting the alias arguments if any + if [[ -n $alias_args ]]; then + compl_func="${new_completion/#* -F /}" + compl_func="${compl_func%% *}" + # avoid recursive call loops by ignoring our own functions + if [[ "${compl_func#_"$namespace"::}" == "$compl_func" ]]; then + compl_wrapper="_${namespace}::${alias_name}" + echo "function $compl_wrapper { + local compl_word=\${2?} + local prec_word=\${3?} + # check if prec_word is the alias itself. if so, replace it + # with the last word in the unaliased form, i.e., + # alias_cmd + ' ' + alias_args. + if [[ \$COMP_LINE == \"\$prec_word \$compl_word\" ]]; then + prec_word='$alias_cmd $alias_args' + prec_word=\${prec_word#* } + fi + (( COMP_CWORD += ${#alias_arg_words[@]} )) + COMP_WORDS=(\"$alias_cmd\" \"${alias_arg_words[@]}\" \"\${COMP_WORDS[@]:1}\") + (( COMP_POINT -= \${#COMP_LINE} )) + COMP_LINE=\${COMP_LINE/$alias_name/$alias_cmd $alias_args} + (( COMP_POINT += \${#COMP_LINE} )) + \"$compl_func\" \"$alias_cmd\" \"\$compl_word\" \"\$prec_word\" + }" >> "$tmp_file" + new_completion="${new_completion/ -F $compl_func / -F $compl_wrapper }" + fi + fi + + # replace completion trigger by alias + if [[ -n $new_completion ]]; then + new_completion="${new_completion% *} $alias_name" + echo "$new_completion" >> "$tmp_file" + fi + done < <(alias -p) + # shellcheck source=/dev/null + source "$tmp_file" && command rm -f "$tmp_file" +} + +_bash-it-component-completion-callback-on-init-aliases diff --git a/completion/available/awscli.completion.bash b/completion/available/awscli.completion.bash index 530bdd27..6b2c90ff 100644 --- a/completion/available/awscli.completion.bash +++ b/completion/available/awscli.completion.bash @@ -1 +1,5 @@ -[[ -x "$(which aws_completer)" ]] && complete -C "$(which aws_completer)" aws +# shellcheck shell=bash + +if _command_exists aws_completer; then + complete -C "$(command -v aws_completer)" aws +fi diff --git a/completion/available/bash-it.completion.bash b/completion/available/bash-it.completion.bash index 4fdd72d6..2259e37b 100644 --- a/completion/available/bash-it.completion.bash +++ b/completion/available/bash-it.completion.bash @@ -1,140 +1,95 @@ -#!/usr/bin/env bash +# shellcheck shell=bash -_bash-it-comp-enable-disable() -{ - local enable_disable_args="alias completion plugin" - COMPREPLY=( $(compgen -W "${enable_disable_args}" -- ${cur}) ) +function _compreply_candidates() { + local IFS=$'\n' + + read -d '' -ra COMPREPLY < <(compgen -W "${candidates[*]}" -- "${cur}") } -_bash-it-comp-list-available-not-enabled() -{ - subdirectory="$1" +function _bash-it() { + local cur prev verb file_type candidates suffix + COMPREPLY=() + cur="${COMP_WORDS[COMP_CWORD]}" + prev="${COMP_WORDS[COMP_CWORD - 1]}" + verb="${COMP_WORDS[1]}" + file_type="${COMP_WORDS[2]:-}" + candidates=('disable' 'enable' 'help' 'migrate' 'reload' 'restart' 'preview' 'profile' 'doctor' 'search' 'show' 'update' 'version') + case "${verb}" in + show) + candidates=('aliases' 'completions' 'plugins') + _compreply_candidates + ;; + help) + if [[ "${prev}" == "aliases" ]]; then + candidates=('all' "$(_bash-it-component-list "${file_type}")") + _compreply_candidates + else + candidates=('aliases' 'completions' 'migrate' 'plugins' 'update') + _compreply_candidates + fi + ;; + profile) + case "${file_type}" in + load | rm) + if [[ "${file_type}" == "$prev" ]]; then + candidates=("${BASH_IT}/profiles"/*.bash_it) + candidates=("${candidates[@]##*/}") + candidates=("${candidates[@]%%.bash_it}") - local available_things - - available_things=$(for f in `compgen -G "${BASH_IT}/$subdirectory/available/*.bash" | sort -d`; - do - file_entity=$(basename $f) - - typeset enabled_component=$(command ls "${BASH_IT}/$subdirectory/enabled/"{[0-9]*$BASH_IT_LOAD_PRIORITY_SEPARATOR$file_entity,$file_entity} 2>/dev/null | head -1) - typeset enabled_component_global=$(command ls "${BASH_IT}/enabled/"[0-9]*$BASH_IT_LOAD_PRIORITY_SEPARATOR$file_entity 2>/dev/null | head -1) - - if [ -z "$enabled_component" ] && [ -z "$enabled_component_global" ] - then - basename $f | sed -e 's/\(.*\)\..*\.bash/\1/g' - fi - done) - - COMPREPLY=( $(compgen -W "all ${available_things}" -- ${cur}) ) -} - -_bash-it-comp-list-enabled() -{ - local subdirectory="$1" - local suffix enabled_things - - suffix=$(echo "$subdirectory" | sed -e 's/plugins/plugin/g') - - enabled_things=$(for f in `sort -d <(compgen -G "${BASH_IT}/$subdirectory/enabled/*.${suffix}.bash") <(compgen -G "${BASH_IT}/enabled/*.${suffix}.bash")`; - do - basename $f | sed -e 's/\(.*\)\..*\.bash/\1/g' | sed -e "s/^[0-9]*---//g" - done) - - COMPREPLY=( $(compgen -W "all ${enabled_things}" -- ${cur}) ) -} - -_bash-it-comp-list-available() -{ - subdirectory="$1" - - local enabled_things - - enabled_things=$(for f in `compgen -G "${BASH_IT}/$subdirectory/available/*.bash" | sort -d`; - do - basename $f | sed -e 's/\(.*\)\..*\.bash/\1/g' - done) - - COMPREPLY=( $(compgen -W "${enabled_things}" -- ${cur}) ) -} - -_bash-it-comp() -{ - local cur prev opts - COMPREPLY=() - cur="${COMP_WORDS[COMP_CWORD]}" - prev="${COMP_WORDS[COMP_CWORD-1]}" - chose_opt="${COMP_WORDS[1]}" - file_type="${COMP_WORDS[2]}" - opts="disable enable help migrate reload restart doctor search show update version" - case "${chose_opt}" in - show) - local show_args="aliases completions plugins" - COMPREPLY=( $(compgen -W "${show_args}" -- ${cur}) ) - return 0 - ;; - help) - if [ x"${prev}" == x"aliases" ]; then - _bash-it-comp-list-available aliases - return 0 - else - local help_args="aliases completions migrate plugins update" - COMPREPLY=( $(compgen -W "${help_args}" -- ${cur}) ) - return 0 - fi - ;; - doctor) - local doctor_args="errors warnings all" - COMPREPLY=( $(compgen -W "${doctor_args}" -- ${cur}) ) - return 0 - ;; - update) - if [[ ${cur} == -* ]];then - local update_args="-s --silent" - else - local update_args="stable dev" - fi - COMPREPLY=( $(compgen -W "${update_args}" -- ${cur}) ) - return 0 - ;; - migrate | reload | search | version) - return 0 - ;; - enable | disable) - if [ x"${chose_opt}" == x"enable" ];then - suffix="available-not-enabled" - else - suffix="enabled" - fi - case "${file_type}" in - alias) - _bash-it-comp-list-${suffix} aliases - return 0 - ;; - plugin) - _bash-it-comp-list-${suffix} plugins - return 0 - ;; - completion) - _bash-it-comp-list-${suffix} completion - return 0 - ;; - *) - _bash-it-comp-enable-disable - return 0 - ;; - esac - ;; - esac - - COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) - - return 0 + _compreply_candidates + fi + ;; + save | list) ;; + *) + candidates=('load' 'save' 'list' 'rm') + _compreply_candidates + ;; + esac + ;; + doctor) + candidates=('errors' 'warnings' 'all') + _compreply_candidates + ;; + update) + if [[ "${cur}" == -* ]]; then + candidates=('-s' '--silent') + else + candidates=('stable' 'dev') + fi + _compreply_candidates + ;; + migrate | reload | restart | search | version) ;; + preview) + _bash-it-preview # completes itself + return 0 + ;; + enable | disable) + if [[ "${verb}" == "enable" ]]; then + suffix="disabled" + else + suffix="enabled" + fi + case "${file_type}" in + alias | completion | plugin) + candidates=('all' "$("_bash-it-component-list-${suffix}" "${file_type}")") + _compreply_candidates + ;; + *) + candidates=('alias' 'completion' 'plugin') + _compreply_candidates + ;; + esac + ;; + *) + _compreply_candidates + ;; + esac } # Activate completion for bash-it and its common misspellings -complete -F _bash-it-comp bash-it -complete -F _bash-it-comp bash-ti -complete -F _bash-it-comp shit -complete -F _bash-it-comp bashit -complete -F _bash-it-comp batshit -complete -F _bash-it-comp bash_it +complete -F _bash-it bash-it +complete -F _bash-it bash-ti +complete -F _bash-it shit +complete -F _bash-it bashit +complete -F _bash-it batshit +complete -F _bash-it bash_it diff --git a/completion/available/composer.completion.bash b/completion/available/composer.completion.bash index eefe50fa..176f0832 100644 --- a/completion/available/composer.completion.bash +++ b/completion/available/composer.completion.bash @@ -3,14 +3,14 @@ cite "about-completion" about-completion "composer completion" function __composer_completion() { - local cur coms opts com + local cur coms opts com words COMPREPLY=() _get_comp_words_by_ref -n : cur words # lookup for command for word in "${words[@]:1}"; do - if [[ $word != -* ]]; then - com=$word + if [[ "${word}" != -* ]]; then + com="${word}" break fi done @@ -19,7 +19,7 @@ function __composer_completion() { if [[ ${cur} == --* ]]; then opts="--help --quiet --verbose --version --ansi --no-ansi --no-interaction --profile --no-plugins --working-dir" - case "$com" in + case "${com}" in about) opts="${opts} " ;; @@ -109,18 +109,18 @@ function __composer_completion() { # shellcheck disable=SC2207 COMPREPLY=($(compgen -W "${opts}" -- "${cur}")) - __ltrim_colon_completions "$cur" + __ltrim_colon_completions "${cur}" return 0 fi # completing for a command - if [[ "$cur" == "$com" ]]; then + if [[ "${cur}" == "${com}" ]]; then coms="about archive browse clear-cache config create-project depends diagnose dump-autoload exec global help init install licenses list outdated prohibits remove require run-script search self-update show status suggests update validate" # shellcheck disable=SC2207 COMPREPLY=($(compgen -W "${coms}" -- "${cur}")) - __ltrim_colon_completions "$cur" + __ltrim_colon_completions "${cur}" return 0 fi diff --git a/completion/available/consul.completion.bash b/completion/available/consul.completion.bash index 3697ffc5..511cf372 100644 --- a/completion/available/consul.completion.bash +++ b/completion/available/consul.completion.bash @@ -3,5 +3,5 @@ cite "about-completion" about-completion "Hashicorp consul completion" if _command_exists consul; then - complete -C "$(which consul)" consul + complete -C "$(command -v consul)" consul fi diff --git a/completion/available/dart.completion.bash b/completion/available/dart.completion.bash index ece96d5c..b7563443 100644 --- a/completion/available/dart.completion.bash +++ b/completion/available/dart.completion.bash @@ -1,7 +1,9 @@ # shellcheck shell=bash __dart_completion() { + # shellcheck disable=SC2155 local prev=$(_get_pword) + # shellcheck disable=SC2155 local curr=$(_get_cword) local HELP="--help -h" diff --git a/completion/available/defaults.completion.bash b/completion/available/defaults.completion.bash index c43c0aae..39d7ea95 100644 --- a/completion/available/defaults.completion.bash +++ b/completion/available/defaults.completion.bash @@ -1,175 +1,5 @@ -# defaults -# Bash command line completion for defaults -# -# Created by Jonathon Mah on 2006-11-08. -# Copyright 2006 Playhaus. All rights reserved. -# -# Version 1.0 (2006-11-08) +# shellcheck shell=bash - -_defaults_domains() -{ - local cur - COMPREPLY=() - cur=${COMP_WORDS[COMP_CWORD]} - - local domains=$( defaults domains | sed -e 's/, /:/g' | tr : '\n' | sed -e 's/ /\\ /g' | grep "^$cur" ) - local IFS=$'\n' - COMPREPLY=( $domains ) - if [[ $( echo '-app' | grep "^$cur" ) ]]; then - COMPREPLY[${#COMPREPLY[@]}]="-app" - fi - - return 0 -} - - -_defaults() -{ - local cur prev host_opts cmds cmd domain keys key_index - cur=${COMP_WORDS[COMP_CWORD]} - prev=${COMP_WORDS[COMP_CWORD-1]} - - host_opts='-currentHost -host' - cmds='read read-type write rename delete domains find help' - - if [[ $COMP_CWORD -eq 1 ]]; then - COMPREPLY=( $( compgen -W "$host_opts $cmds" -- $cur ) ) - return 0 - elif [[ $COMP_CWORD -eq 2 ]]; then - if [[ "$prev" == "-currentHost" ]]; then - COMPREPLY=( $( compgen -W "$cmds" -- $cur ) ) - return 0 - elif [[ "$prev" == "-host" ]]; then - _known_hosts -a - return 0 - else - _defaults_domains - return 0 - fi - elif [[ $COMP_CWORD -eq 3 ]]; then - if [[ ${COMP_WORDS[1]} == "-host" ]]; then - _defaults_domains - return 0 - fi - fi - - # Both a domain and command have been specified - - if [[ ${COMP_WORDS[1]} == [${cmds// /|}] ]]; then - cmd=${COMP_WORDS[1]} - domain=${COMP_WORDS[2]} - key_index=3 - if [[ "$domain" == "-app" ]]; then - if [[ $COMP_CWORD -eq 3 ]]; then - # Completing application name. Can't help here, sorry - return 0 - fi - domain="-app ${COMP_WORDS[3]}" - key_index=4 - fi - elif [[ ${COMP_WORDS[2]} == "-currentHost" ]] && [[ ${COMP_WORDS[2]} == [${cmds// /|}] ]]; then - cmd=${COMP_WORDS[2]} - domain=${COMP_WORDS[3]} - key_index=4 - if [[ "$domain" == "-app" ]]; then - if [[ $COMP_CWORD -eq 4 ]]; then - # Completing application name. Can't help here, sorry - return 0 - fi - domain="-app ${COMP_WORDS[4]}" - key_index=5 - fi - elif [[ ${COMP_WORDS[3]} == "-host" ]] && [[ ${COMP_WORDS[3]} == [${cmds// /|}] ]]; then - cmd=${COMP_WORDS[3]} - domain=${COMP_WORDS[4]} - key_index=5 - if [[ "$domain" == "-app" ]]; then - if [[ $COMP_CWORD -eq 5 ]]; then - # Completing application name. Can't help here, sorry - return 0 - fi - domain="-app ${COMP_WORDS[5]}" - key_index=6 - fi - fi - - keys=$( defaults read $domain 2>/dev/null | sed -n -e '/^ [^}) ]/p' | sed -e 's/^ \([^" ]\{1,\}\) = .*$/\1/g' -e 's/^ "\([^"]\{1,\}\)" = .*$/\1/g' | sed -e 's/ /\\ /g' ) - - case $cmd in - read|read-type) - # Complete key - local IFS=$'\n' - COMPREPLY=( $( echo "$keys" | grep -i "^${cur//\\/\\\\}" ) ) - ;; - write) - if [[ $key_index -eq $COMP_CWORD ]]; then - # Complete key - local IFS=$'\n' - COMPREPLY=( $( echo "$keys" | grep -i "^${cur//\\/\\\\}" ) ) - elif [[ $((key_index+1)) -eq $COMP_CWORD ]]; then - # Complete value type - # Unfortunately ${COMP_WORDS[key_index]} fails on keys with spaces - local value_types='-string -data -integer -float -boolean -date -array -array-add -dict -dict-add' - local cur_type=$( defaults read-type $domain ${COMP_WORDS[key_index]} 2>/dev/null | sed -e 's/^Type is \(.*\)/-\1/' -e's/dictionary/dict/' | grep "^$cur" ) - if [[ $cur_type ]]; then - COMPREPLY=( $cur_type ) - else - COMPREPLY=( $( compgen -W "$value_types" -- $cur ) ) - fi - elif [[ $((key_index+2)) -eq $COMP_CWORD ]]; then - # Complete value - # Unfortunately ${COMP_WORDS[key_index]} fails on keys with spaces - COMPREPLY=( $( defaults read $domain ${COMP_WORDS[key_index]} 2>/dev/null | grep -i "^${cur//\\/\\\\}" ) ) - fi - ;; - rename) - if [[ $key_index -eq $COMP_CWORD ]] || - [[ $((key_index+1)) -eq $COMP_CWORD ]]; then - # Complete source and destination keys - local IFS=$'\n' - COMPREPLY=( $( echo "$keys" | grep -i "^${cur//\\/\\\\}" ) ) - fi - ;; - delete) - if [[ $key_index -eq $COMP_CWORD ]]; then - # Complete key - local IFS=$'\n' - COMPREPLY=( $( echo "$keys" | grep -i "^${cur//\\/\\\\}" ) ) - fi - ;; - esac - - return 0 -} - -complete -F _defaults -o default defaults - - -# This file is licensed under the BSD license, as follows: -# -# Copyright (c) 2006, Playhaus -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, this -# list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# * Neither the name of the Playhaus nor the names of its contributors may be -# used to endorse or promote products derived from this software without -# specific prior written permission. -# -# This software is provided by the copyright holders and contributors "as is" -# and any express or implied warranties, including, but not limited to, the -# implied warranties of merchantability and fitness for a particular purpose are -# disclaimed. In no event shall the copyright owner or contributors be liable -# for any direct, indirect, incidental, special, exemplary, or consequential -# damages (including, but not limited to, procurement of substitute goods or -# services; loss of use, data, or profits; or business interruption) however -# caused and on any theory of liability, whether in contract, strict liability, -# or tort (including negligence or otherwise) arising in any way out of the use -# of this software, even if advised of the possibility of such damage. +if test -s "${BASH_IT?}/vendor/github.com/gaelicWizard/bash-progcomp/defaults.completion.bash"; then + source "$_" +fi diff --git a/completion/available/dmidecode.completion.bash b/completion/available/dmidecode.completion.bash index fc50c1f0..4a884524 100644 --- a/completion/available/dmidecode.completion.bash +++ b/completion/available/dmidecode.completion.bash @@ -1,7 +1,9 @@ # shellcheck shell=bash function __dmidecode_completion() { + # shellcheck disable=SC2155 local prev=$(_get_pword) + # shellcheck disable=SC2155 local curr=$(_get_cword) case $prev in diff --git a/completion/available/docker-compose.completion.bash b/completion/available/docker-compose.completion.bash old mode 100644 new mode 100755 index bf2c13fb..1102f5d9 --- a/completion/available/docker-compose.completion.bash +++ b/completion/available/docker-compose.completion.bash @@ -676,7 +676,7 @@ _docker_compose() { done local completions_func=_docker_compose_${command//-/_} - declare -F $completions_func >/dev/null && $completions_func + _is_function $completions_func && $completions_func eval "$previous_extglob_setting" return 0 diff --git a/completion/available/dotnet.completion.bash b/completion/available/dotnet.completion.bash new file mode 100644 index 00000000..362cfc92 --- /dev/null +++ b/completion/available/dotnet.completion.bash @@ -0,0 +1,14 @@ +# shellcheck shell=bash +about-completion "bash parameter completion for the dotnet CLI" +# see https://docs.microsoft.com/en-us/dotnet/core/tools/enable-tab-autocomplete#bash + +function _dotnet_bash_complete() { + local cur="${COMP_WORDS[COMP_CWORD]}" IFS=$'\n' + local candidates + + read -d '' -ra candidates < <(dotnet complete --position "${COMP_POINT}" "${COMP_LINE}" 2> /dev/null) + + read -d '' -ra COMPREPLY < <(compgen -W "${candidates[*]:-}" -- "$cur") +} + +complete -f -F _dotnet_bash_complete dotnet diff --git a/completion/available/fabric.completion.bash b/completion/available/fabric.completion.bash old mode 100755 new mode 100644 diff --git a/completion/available/git.completion.bash b/completion/available/git.completion.bash old mode 100755 new mode 100644 index b9bb1bb6..31b77fa3 --- a/completion/available/git.completion.bash +++ b/completion/available/git.completion.bash @@ -1,39 +1,42 @@ -#!/usr/bin/env bash - -# Only operate on MacOS since there are no linux paths -if [[ "$OSTYPE" != 'darwin'* ]] ; then - _log_warning "unsupported operating system - only 'Darwin' is supported" - return 0 -fi +# shellcheck shell=bash +# +# Locate and load completions for `git`. # Make sure git is installed -_command_exists git || return 0 +_command_exists git || return # Don't handle completion if it's already managed -if complete -p git &>/dev/null ; then - _log_warning "completion already loaded - this usually means it is safe to stop using this completion" - return 0 +if complete -p git &> /dev/null; then + _log_warning "completion already loaded - this usually means it is safe to stop using this completion" + return 0 fi -_git_bash_completion_found=false +_git_bash_completion_xcrun_git= +if _command_exists xcrun; then + _git_bash_completion_xcrun_git="$(xcrun --find git)" +fi _git_bash_completion_paths=( - # MacOS non-system locations - '/Library/Developer/CommandLineTools/usr/share/git-core/git-completion.bash' - '/Applications/Xcode.app/Contents/Developer/usr/share/git-core/git-completion.bash' + # Standard locations + "${GIT_EXE%/*}/../share/git-core/git-completion.bash" + "${GIT_EXE%/*}/../share/git-core/contrib/completion/git-completion.bash" + "${GIT_EXE%/*}/../etc/bash_completion.d/git-completion.bash" + # MacOS non-system locations + "${_git_bash_completion_xcrun_git%/bin/git}/share/git-core/git-completion.bash" ) # Load the first completion file found -for _comp_path in "${_git_bash_completion_paths[@]}" ; do - if [[ -r "$_comp_path" ]] ; then - _git_bash_completion_found=true - source "$_comp_path" - break - fi +_git_bash_completion_found=false +for _comp_path in "${_git_bash_completion_paths[@]}"; do + if [[ -r "$_comp_path" ]]; then + _git_bash_completion_found=true + # shellcheck disable=SC1090 # don't follow + source "$_comp_path" + break + fi done # Cleanup if [[ "${_git_bash_completion_found}" == false ]]; then - _log_warning "no completion files found - please try enabling the 'system' completion instead." + _log_warning "no completion files found - please try enabling the 'system' completion instead." fi -unset _git_bash_completion_paths -unset _git_bash_completion_found +unset "${!_git_bash_completion@}" diff --git a/completion/available/git_flow.completion.bash b/completion/available/git_flow.completion.bash index 04f20ccd..7bfc9ef4 100644 --- a/completion/available/git_flow.completion.bash +++ b/completion/available/git_flow.completion.bash @@ -172,6 +172,7 @@ __git_flow_list_hotfixes () } # temporarily wrap __git_find_on_cmdline() for backwards compatibility -if [ -z "`type -t __git_find_subcommand`" ]; then +if ! _command_exists __git_find_subcommand +then alias __git_find_subcommand=__git_find_on_cmdline fi diff --git a/completion/available/git_flow_avh.completion.bash b/completion/available/git_flow_avh.completion.bash index 0b73a0be..abb51bf8 100644 --- a/completion/available/git_flow_avh.completion.bash +++ b/completion/available/git_flow_avh.completion.bash @@ -505,6 +505,7 @@ __git_flow_list_branches () } # alias __git_find_on_cmdline for backwards compatibility -if [ -z "`type -t __git_find_on_cmdline`" ]; then +if ! _command_exists __git_find_on_cmdline +then alias __git_find_on_cmdline=__git_find_subcommand fi diff --git a/completion/available/gradle.completion.bash b/completion/available/gradle.completion.bash index 2b33383f..35971d50 100644 --- a/completion/available/gradle.completion.bash +++ b/completion/available/gradle.completion.bash @@ -22,17 +22,9 @@ # Avoid inaccurate completions for subproject tasks COMP_WORDBREAKS=$(echo "$COMP_WORDBREAKS" | sed -e 's/://g') -__gradle-set-project-root-dir() { - local dir=`pwd` - project_root_dir=`pwd` - while [[ $dir != '/' ]]; do - if [[ -f "$dir/settings.gradle" || -f "$dir/gradlew" ]]; then - project_root_dir=$dir - return 0 - fi - dir="$(dirname "$dir")" - done - return 1 +function __gradle-set-project-root-dir() { + project_root_dir="$(_bash-it-find-in-ancestor "settings.gradle" "gradlew")" + return "$?" } __gradle-init-cache-dir() { @@ -58,9 +50,9 @@ __gradle-set-cache-name() { __gradle-set-files-checksum() { # Cache MD5 sum of all Gradle scripts and modified timestamps - if builtin command -v md5 > /dev/null; then + if _command_exists md5; then gradle_files_checksum=$(md5 -q -s "$(cat "$cache_dir/$cache_name" | xargs ls -o 2>/dev/null)") - elif builtin command -v md5sum > /dev/null; then + elif _command_exists md5sum; then gradle_files_checksum=$(cat "$cache_dir/$cache_name" | xargs ls -o 2>/dev/null | md5sum | awk '{print $1}') else echo "Cannot generate completions as neither md5 nor md5sum exist on \$PATH" diff --git a/completion/available/hub.completion.bash b/completion/available/hub.completion.bash index 79d76bf0..67a5e29b 100644 --- a/completion/available/hub.completion.bash +++ b/completion/available/hub.completion.bash @@ -23,12 +23,12 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # If there is no git tab completion, but we have the _completion loader try to load it -if ! declare -F _git > /dev/null && declare -F _completion_loader > /dev/null; then +if ! _is_function _git && _is_function _completion_loader; then _completion_loader git fi # Check that git tab completion is available and we haven't already set up completion -if declare -F _git > /dev/null && ! declare -F __git_list_all_commands_without_hub > /dev/null; then +if _is_function _git && ! _is_function __git_list_all_commands_without_hub; then # Duplicate and rename the 'list_all_commands' function eval "$(declare -f __git_list_all_commands | \ sed 's/__git_list_all_commands/__git_list_all_commands_without_hub/')" @@ -227,7 +227,7 @@ EOF ((c++)) done if [ -z "$name" ]; then - repo=$(basename "$(pwd)") + repo="$(basename "${PWD}")" fi case "$prev" in -d|-h) diff --git a/completion/available/knife.completion.bash b/completion/available/knife.completion.bash index 83b332f9..c0fb6a99 100644 --- a/completion/available/knife.completion.bash +++ b/completion/available/knife.completion.bash @@ -42,6 +42,7 @@ _KAC_is_file_newer_than() { _KAC_regen_cache() { local CACHE_NAME=$1 local CACHE_PATH="$_KNIFE_AUTOCOMPLETE_CACHE_DIR/$CACHE_NAME" + # shellcheck disable=SC2155 local TMP_FILE=$(mktemp "$_KAC_CACHE_TMP_DIR/$CACHE_NAME.XXXX") shift 1 # discard the temp file if it's empty AND the previous command didn't exit successfully, but still mark the cache as updated @@ -54,18 +55,19 @@ _KAC_regen_cache() { # cached files can't have spaces in their names _KAC_get_cache_name_from_command() { - echo "${@/ /_SPACE_}" + echo "${@// /_SPACE_}" } # the reverse operation from the function above _KAC_get_command_from_cache_name() { - echo "${@/_SPACE_/ }" + echo "${@//_SPACE_/ }" } # given a command as argument, it fetches the cache for that command if it can find it # otherwise it waits for the cache to be generated # in either case, it regenerates the cache, and sets the _KAC_CACHE_PATH env variable # for obvious reason, do NOT call that in a sub-shell (in particular, no piping) +# shellcheck disable=SC2155 _KAC_get_and_regen_cache() { # the cache name can't have space in it local CACHE_NAME=$(_KAC_get_cache_name_from_command "$@") @@ -100,7 +102,7 @@ _KAC_clean_cache() { # perform a cache cleaning when loading this file # On big systems this could baloon up to a 30 second run or more, so not enabling by default. -[[ "${KNIFE_CACHE_CLEAN}" ]] && _KAC_clean_cache +[[ -n "${KNIFE_CACHE_CLEAN}" ]] && _KAC_clean_cache ##################################### ### End of cache helper functions ### @@ -118,7 +120,7 @@ _KAC_get_current_base_command() { local PREVIOUS="knife" local I=1 local CURRENT - while [ $I -le "$COMP_CWORD" ]; do + while [[ "${I}" -le "${COMP_CWORD}" ]]; do # command words are all lower-case echo "${COMP_WORDS[$I]}" | grep -E "^[a-z]+$" > /dev/null || break CURRENT="$PREVIOUS ${COMP_WORDS[$I]}" @@ -127,12 +129,13 @@ _KAC_get_current_base_command() { I=$((I + 1)) done _KAC_CURRENT_COMMAND=$PREVIOUS - [ $I -le "$COMP_CWORD" ] && _KAC_CURRENT_COMMAND_NB_WORDS=$I + [[ "${I}" -le "${COMP_CWORD}" ]] && _KAC_CURRENT_COMMAND_NB_WORDS="${I}" } # searches the position of the currently completed argument in the current base command # (i.e. handles "plural" arguments such as knife cookbook upload cookbook1 cookbook2 and so on...) # assumes the current base command is complete +# shellcheck disable=SC2155 _KAC_get_current_arg_position() { local CURRENT_ARG_POS=$((_KAC_CURRENT_COMMAND_NB_WORDS + 1)) local COMPLETE_COMMAND=$(grep -E "^$_KAC_CURRENT_COMMAND" "$_KAC_CACHE_PATH") @@ -150,10 +153,11 @@ _KAC_get_current_arg_position() { _knife() { _KAC_get_and_regen_cache _KAC_knife_commands local RAW_LIST ITEM REGEN_CMD ARG_POSITION + # shellcheck disable=SC2034 COMREPLY=() # get correct command & arg pos _KAC_get_current_base_command && ARG_POSITION=$(_KAC_get_current_arg_position) || ARG_POSITION=$((COMP_CWORD + 1)) - RAW_LIST=$(grep -E "^$_KAC_CURRENT_COMMAND" "$_KAC_CACHE_PATH" | cut -d ' ' -f $ARG_POSITION | uniq) + RAW_LIST=$(grep -E "^${_KAC_CURRENT_COMMAND}" "${_KAC_CACHE_PATH}" | cut -d ' ' -f "${ARG_POSITION}" | uniq) # we need to process that raw list a bit, most notably for placeholders # NOTE: I chose to explicitely fetch & cache _certain_ informations for the server (cookbooks & node names, etc) diff --git a/completion/available/laravel.completion.bash b/completion/available/laravel.completion.bash index 7bd6f223..8f032568 100644 --- a/completion/available/laravel.completion.bash +++ b/completion/available/laravel.completion.bash @@ -1,15 +1,16 @@ -#!/usr/bin/bash +# shellcheck shell=bash -if command -v laravel > /dev/null; then - __laravel_completion() { - local OPTS=("-h --help -q --quiet --ansi --no-ansi -n --no-interaction -v -vv -vvv --verbose help list new") - COMPREPLY=() - for _opt_ in ${OPTS[@]}; do - if [[ "$_opt_" == "$2"* ]]; then - COMPREPLY+=("$_opt_") - fi - done - } +_command_exists laravel || return - complete -F __laravel_completion laravel -fi +function __laravel_completion() { + local OPTS=('-h' '--help' '-q' '--quiet' '--ansi' '--no-ansi' '-n' '--no-interaction' '-v' '-vv' '-vvv' '--verbose' 'help' 'list' 'new') + local _opt_ + COMPREPLY=() + for _opt_ in "${OPTS[@]}"; do + if [[ "$_opt_" == "$2"* ]]; then + COMPREPLY+=("$_opt_") + fi + done +} + +complete -F __laravel_completion laravel diff --git a/completion/available/ngrok.completion.bash b/completion/available/ngrok.completion.bash index ffbdd3c8..ca50a16f 100644 --- a/completion/available/ngrok.completion.bash +++ b/completion/available/ngrok.completion.bash @@ -1,7 +1,9 @@ # shellcheck shell=bash __ngrok_completion() { + # shellcheck disable=SC2155 local prev=$(_get_pword) + # shellcheck disable=SC2155 local curr=$(_get_cword) local BASE_NO_CONF="--log --log-format --log-level --help" diff --git a/completion/available/notify-send.completion.bash b/completion/available/notify-send.completion.bash index b2c171e9..676485f8 100644 --- a/completion/available/notify-send.completion.bash +++ b/completion/available/notify-send.completion.bash @@ -1,7 +1,9 @@ # shellcheck shell=bash function __notify-send_completions() { + # shellcheck disable=SC2155 local curr=$(_get_cword) + # shellcheck disable=SC2155 local prev=$(_get_pword) case $prev in diff --git a/completion/available/pew.completion.bash b/completion/available/pew.completion.bash index 73d62e39..04e67ecb 100644 --- a/completion/available/pew.completion.bash +++ b/completion/available/pew.completion.bash @@ -1 +1,6 @@ -[[ -x "$(which pew)" ]] && source "$(pew shell_config)" +# shellcheck shell=bash + +if _command_exists pew +then + source "$(pew shell_config)" +fi diff --git a/completion/available/pip.completion.bash b/completion/available/pip.completion.bash index 7ed8c17e..f094d6ed 100644 --- a/completion/available/pip.completion.bash +++ b/completion/available/pip.completion.bash @@ -6,6 +6,15 @@ # If the pip package is installed within virtual environments, say, python managed by pyenv, # you should first initialize the corresponding environment. # So that pip is in the system's path. -if _command_exists pip; then - eval "$(pip completion --bash)" -fi +_command_exists pip || return + +function __bash_it_complete_pip() { + if _command_exists _pip_completion; then + complete -o default -F _pip_completion pip + _pip_completion "$@" + else + eval "$(pip completion --bash)" + _pip_completion "$@" + fi +} +complete -o default -F __bash_it_complete_pip pip diff --git a/completion/available/pip3.completion.bash b/completion/available/pip3.completion.bash index c2b58564..34abc053 100644 --- a/completion/available/pip3.completion.bash +++ b/completion/available/pip3.completion.bash @@ -6,6 +6,15 @@ # If the pip package is installed within virtual environments, say, python managed by pyenv, # you should first initialize the corresponding environment. # So that pip3 is in the system's path. -if _command_exists pip3; then - eval "$(pip3 completion --bash)" -fi +_command_exists pip3 || return + +function __bash_it_complete_pip3() { + if _command_exists _pip_completion; then + complete -o default -F _pip_completion pip3 + _pip_completion "$@" + else + eval "$(pip3 completion --bash)" + _pip_completion "$@" + fi +} +complete -o default -F __bash_it_complete_pip3 pip3 diff --git a/completion/available/pipenv.completion.bash b/completion/available/pipenv.completion.bash index 52451b8a..4adfab95 100644 --- a/completion/available/pipenv.completion.bash +++ b/completion/available/pipenv.completion.bash @@ -1,4 +1,4 @@ # shellcheck shell=bash if _command_exists pipenv; then - eval "$(pipenv --completion)" + eval "$(_PIPENV_COMPLETE=bash_source pipenv)" fi diff --git a/completion/available/sdkman.completion.bash b/completion/available/sdkman.completion.bash index 7f0157f3..2dc09088 100644 --- a/completion/available/sdkman.completion.bash +++ b/completion/available/sdkman.completion.bash @@ -1,7 +1,9 @@ # shellcheck shell=bash -_sdkman_complete() { + +function _sdkman_complete() { local CANDIDATES local CANDIDATE_VERSIONS + local SDKMAN_CANDIDATES_CSV="${SDKMAN_CANDIDATES_CSV:-}" COMPREPLY=() @@ -10,7 +12,7 @@ _sdkman_complete() { elif [ "$COMP_CWORD" -eq 2 ]; then case "${COMP_WORDS[COMP_CWORD - 1]}" in "install" | "i" | "uninstall" | "rm" | "list" | "ls" | "use" | "u" | "default" | "d" | "home" | "h" | "current" | "c" | "upgrade" | "ug") - CANDIDATES=$(echo "${SDKMAN_CANDIDATES_CSV}" | tr ',' ' ') + CANDIDATES="${SDKMAN_CANDIDATES_CSV//,/${IFS:0:1}}" mapfile -t COMPREPLY < <(compgen -W "$CANDIDATES" -- "${COMP_WORDS[COMP_CWORD]}") ;; "env") @@ -46,17 +48,17 @@ _sdkman_complete() { return 0 } -_sdkman_candidate_local_versions() { +function _sdkman_candidate_local_versions() { CANDIDATE_VERSIONS=$(__sdkman_cleanup_local_versions "$1") } -_sdkman_candidate_all_versions() { +function _sdkman_candidate_all_versions() { candidate="$1" CANDIDATE_LOCAL_VERSIONS=$(__sdkman_cleanup_local_versions "$candidate") - if [ "$SDKMAN_OFFLINE_MODE" = "true" ]; then + if [[ "${SDKMAN_OFFLINE_MODE:-false}" == "true" ]]; then CANDIDATE_VERSIONS=$CANDIDATE_LOCAL_VERSIONS else # sdkman has a specific output format for Java candidate since @@ -70,12 +72,12 @@ _sdkman_candidate_all_versions() { # "+" - local version # "*" - installed # ">" - currently in use - CANDIDATE_VERSIONS="$(echo "$CANDIDATE_ONLINE_VERSIONS $CANDIDATE_LOCAL_VERSIONS" | tr ' ' '\n' | grep -v -e '^[[:space:]|\*|\>|\+]*$' | sort | uniq -u) " + CANDIDATE_VERSIONS="$(echo "$CANDIDATE_ONLINE_VERSIONS $CANDIDATE_LOCAL_VERSIONS" | tr ' ' '\n' | grep -v -e '^[[:space:]|\*|\>|\+]*$' | sort -u) " fi } -__sdkman_cleanup_local_versions() { +function __sdkman_cleanup_local_versions() { __sdkman_build_version_csv "$1" | tr ',' ' ' diff --git a/completion/available/sqlmap.completion.bash b/completion/available/sqlmap.completion.bash index 16addf85..213dd817 100644 --- a/completion/available/sqlmap.completion.bash +++ b/completion/available/sqlmap.completion.bash @@ -1,21 +1,22 @@ -#!/bin/bash +# shellcheck shell=bash # ---------------------------------------------------------------------------+ # | -# Thanks to Alexander Korznikov | +# Thanks to Alexander Korznikov | # http://www.korznikov.com/2014/12/bash-tab-completion-for-awesome-tool.html | # | # ---------------------------------------------------------------------------+ -if command -v sqlmap > /dev/null; then +if _command_exists sqlmap +then - _sqlmap() + function _sqlmap() { local cur prev COMPREPLY=() - cur=$(_get_cword) - prev=$(_get_pword) + cur="$(_get_cword)" + prev="$(_get_pword)" case $prev in @@ -143,7 +144,7 @@ if command -v sqlmap > /dev/null; then --mobile --page-rank --purge-output --smart \ --sqlmap-shell --wizard ' COMPREPLY=( $( \ - (while read -d ' ' i; do + (while read -d ' ' i; do [[ -z "$i" || "${onlyonce/ ${i%% *} / }" == "$onlyonce" ]] && continue # flatten array with spaces on either side, @@ -152,7 +153,7 @@ if command -v sqlmap > /dev/null; then COMPREPLY=" ${COMPREPLY[@]} " # remove word from list of completions COMPREPLY=( ${COMPREPLY/ ${i%% *} / } ) - done + done printf '%s ' "${COMPREPLY[@]}") <<<"${COMP_WORDS[@]}" ) ) diff --git a/completion/available/svn.completion.bash b/completion/available/svn.completion.bash index eabc15c9..2f0a23fe 100644 --- a/completion/available/svn.completion.bash +++ b/completion/available/svn.completion.bash @@ -1,1514 +1,40 @@ -# ------------------------------------------------------------ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at +# shellcheck shell=bash # -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# ------------------------------------------------------------ +# Locate and load completions for `svn`. -# Programmable completion for the Subversion svn command under bash. Source -# this file (or on some systems add it to ~/.bash_completion and start a new -# shell) and bash's completion mechanism will know all about svn's options! -# Provides completion for the svnadmin, svndumpfilter, svnlook and svnsync -# commands as well. Who wants to read man pages/help text... +# Make sure svn is installed +_command_exists svn || return -# Known to work with bash 3.* with programmable completion and extended -# pattern matching enabled (use 'shopt -s extglob progcomp' to enable -# these if they are not already enabled). - -shopt -s extglob - -# Tree helper functions which only use bash, to ease readability. - -# look for value associated to key from stdin in K/V hash file format -# val=$(_svn_read_hashfile svn:realmstring < some/file) -function _svn_read_hashfile() -{ - local tkey=$1 key= val= - while true; do - read tag len - [ $tag = 'END' ] && break - [ $tag != 'K' ] && { - #echo "unexpected tag '$tag' instead of 'K'" >&2 - return - } - read -r -n $len key ; read - read tag len - [ $tag != 'V' ] && { - #echo "unexpected tag '$tag' instead of 'V'" >&2 - return - } - read -r -n $len val ; read - if [[ $key = $tkey ]] ; then - echo "$val" - return - fi - done - #echo "target key '$tkey' not found" >&2 -} - -# _svn_grcut shell-regular-expression -# extract filenames from 'svn status' output -function _svn_grcut() -{ - local re=$1 line= old_IFS - # fix IFS, so that leading spaces are not ignored by next read. - # (there is a leading space in svn status output if only a prop is changed) - old_IFS="$IFS" - IFS=$'\n' - while read -r line ; do - [[ ! $re || $line == $re ]] && echo "${line/????????/}" - done - IFS="$old_IFS" -} - -# extract stuff from svn info output -# _svn_info (URL|Repository Root) -function _svn_info() -{ - local what=$1 line= - LANG=C LC_MESSAGES=C svn info --non-interactive 2> /dev/null | \ - while read line ; do - [[ $line == *"$what: "* ]] && echo ${line#*: } - done -} - -# _svn_lls (dir|file|all) files... -# list svn-managed files from list -# some 'svn status --all-files' would be welcome here? -function _svn_lls() -{ - local opt=$1 f= - shift - for f in "$@" ; do - # could try to check in .svn/entries? hmmm... - if [[ $opt == @(dir|all) && -d "$f" ]] ; then - echo "$f/" - elif [[ $opt == @(file|all) ]] ; then - # split f in directory/file names - local dn= fn="$f" - [[ "$f" == */* ]] && dn=${f%\/*}/ fn=${f##*\/} - # ??? this does not work for just added files, because they - # do not have a content reference yet... - [ -f "${dn}.svn/text-base/${fn}.svn-base" ] && echo "$f" - fi - done -} - -# This completion guides the command/option order along the one suggested -# by "svn help", although other syntaxes are allowed. -# -# - there is a "real" parser to check for what is available and deduce what -# can be suggested further. -# - the syntax should be coherent with subversion/svn/{cl.h,main.c} -# - although it is not a good practice, mixed options and arguments -# is supported by the completion as it is by the svn command. -# - the completion works in the middle of a line, -# but not really in the middle of an argument or option. -# - property names are completed: see comments about issues related to handling -# ":" within property names although it is a word completion separator. -# - unknown properties are assumed to be simple file properties. -# - --revprop and --revision options are forced to revision properties -# as they are mandatory in this case. -# - argument values are suggested to some other options, eg directory names -# for --config-dir. -# - values for some options can be extended with environment variables: -# SVN_BASH_FILE_PROPS: other properties on files/directories -# SVN_BASH_REV_PROPS: other properties on revisions -# SVN_BASH_ENCODINGS: encodings to be suggested -# SVN_BASH_MIME_TYPE: mime types to be suggested -# SVN_BASH_KEYWORDS: "svn:keywords" substitutions to be suggested -# SVN_BASH_USERNAME: usernames suggested for --username -# SVN_BASH_COMPL_EXT: completion extensions for file arguments, based on the -# current subcommand, so that for instance only modified files are -# suggested for 'revert', only not svn-managed files for 'add', and so on. -# Possible values are: -# - username: guess usernames from ~/.subversion/auth/... -# - urls: guess urls from ~/.subversion/auth/... or others -# - svnstatus: use 'svn status' for completion -# - recurse: allow recursion (expensive) -# - externals: recurse into externals (very expensive) -# Former options are reasonable, but beware that both later options -# may be unadvisable if used on large working copies. -# None of these costly completions are activated by default. -# Argument completion outside a working copy results in an error message. -# Filenames with spaces are not completed properly. -# -# TODO -# - other options? -# - obsolete options could be removed from auto-comp? (e.g. -N) -# - obsolete commands could be removed? (e.g. resolved) -# - completion does not work properly when editing in the middle of the line -# status/previous are those at the end of the line, not at the entry position -# - url completion should select more cases where it is relevant -# - url completion of http:// schemas could suggest sub directories? -# - add completion for experimental 'obliterate' feature? -_svn() -{ - local cur cmds cmdOpts pOpts mOpts rOpts qOpts nOpts optsParam opt - - COMPREPLY=() - cur=${COMP_WORDS[COMP_CWORD]} - - # Possible expansions, without pure-prefix abbreviations such as "up". - cmds='add blame annotate praise cat changelist cl checkout co cleanup' - cmds="$cmds commit ci copy cp delete remove rm diff export help import" - cmds="$cmds info list ls lock log merge mergeinfo mkdir move mv rename" - cmds="$cmds patch propdel pdel propedit pedit propget pget proplist" - cmds="$cmds plist propset pset relocate resolve resolved revert status" - cmds="$cmds switch unlock update upgrade" - - # help options have a strange command status... - local helpOpts='--help -h' - # all special options that have a command status - local specOpts="--version $helpOpts" - - # options that require a parameter - # note: continued lines must end '|' continuing lines must start '|' - optsParam="-r|--revision|--username|--password|--targets" - optsParam="$optsParam|-x|--extensions|-m|--message|-F|--file" - optsParam="$optsParam|--encoding|--diff-cmd|--diff3-cmd|--editor-cmd" - optsParam="$optsParam|--old|--new|--config-dir|--config-option" - optsParam="$optsParam|--native-eol|-l|--limit|-c|--change" - optsParam="$optsParam|--depth|--set-depth|--with-revprop" - optsParam="$optsParam|--cl|--changelist|--accept|--show-revs" - - # svn:* and other (env SVN_BASH_*_PROPS) properties - local svnProps revProps allProps psCmds propCmds - - # svn and user configured "file" (or directory) properties - # the "svn:mergeinfo" prop is not included by default because it is - # managed automatically, so there should be no need to edit it by hand. - svnProps="svn:keywords svn:executable svn:needs-lock svn:externals - svn:ignore svn:eol-style svn:mime-type $SVN_BASH_FILE_PROPS" - - # svn and user configured revision properties - revProps="svn:author svn:log svn:date $SVN_BASH_REV_PROPS" - - # all properties as an array variable - allProps=( $svnProps $revProps ) - - # subcommands that expect property names - psCmds='propset|pset|ps' - propCmds="$psCmds|propget|pget|pg|propedit|pedit|pe|propdel|pdel|pd" - - # possible URL schemas to access a subversion server - local urlSchemas='file:/// http:// https:// svn:// svn+ssh://' - - # Parse arguments and set various variables about what was found. - # - # cmd: the current command if available - # isPropCmd: whether it expects a property name argument - # isPsCmd: whether it also expects a property value argument - # isHelpCmd: whether it is about help - # nExpectArgs: how many arguments are expected by the command - # help: help requested about this command (if cmd=='help') - # prop: property name (if appropriate) - # isRevProp: is it a special revision property - # val: property value (if appropriate, under pset) - # options: all options encountered - # hasRevPropOpt: is --revprop set - # hasRevisionOpt: is --revision set - # hasRelocateOpt: is --relocate set - # hasReintegrateOpt: is --reintegrate set - # acceptOpt: the value of --accept - # nargs: how many arguments were found - # stat: status of parsing at the 'current' word - # - # prev: previous command in the loop - # last: status of last parameter analyzed - # i: index - local cmd= isPropCmd= isPsCmd= isHelpCmd= nExpectArgs= isCur= i=0 - local prev= help= prop= val= isRevProp= last='none' nargs=0 stat= - local options= hasRevPropOpt= hasRevisionOpt= hasRelocateOpt= - local acceptOpt= URL= hasReintegrateOpt= - - for opt in "${COMP_WORDS[@]}" - do - # get status of current word (from previous iteration) - [[ $isCur ]] && stat=$last - - # are we processing the current word - isCur= - [[ $i -eq $COMP_CWORD ]] && isCur=1 - let i++ - - # FIRST must be the "svn" command - [ $last = 'none' ] && { last='first'; continue ; } - - # SKIP option arguments - if [[ $prev == @($optsParam) ]] ; then - - # record accept value - [[ $prev = '--accept' ]] && acceptOpt=$opt - - prev='' - last='skip' - continue ; - fi - - # Argh... This looks like a bash bug... - # Redirections are passed to the completion function - # although it is managed by the shell directly... - # It matters because we want to tell the user when no more - # completion is available, so it does not necessary - # fallback to the default case. - if [[ $prev == @(<|>|>>|[12]>|[12]>>) ]] ; then - prev='' - last='skip' - continue ; - fi - prev=$opt - - # get the subCoMmanD - if [[ ! $cmd && $opt \ - && ( $opt != -* || $opt == @(${specOpts// /|}) ) ]] - then - cmd=$opt - [[ $cmd == @($propCmds) ]] && isPropCmd=1 - [[ $cmd == @($psCmds) ]] && isPsCmd=1 - [[ $cmd == @(${helpOpts// /|}) ]] && cmd='help' - [[ $cmd = 'help' ]] && isHelpCmd=1 - # HELP about a command asked with an option - if [[ $isHelpCmd && $cmd && $cmd != 'help' && ! $help ]] - then - help=$cmd - cmd='help' - fi - last='cmd' - continue - fi - - # HELP about a command - if [[ $isHelpCmd && ! $help && $opt && $opt != -* ]] - then - help=$opt - last='help' - continue - fi - - # PROPerty name - if [[ $isPropCmd && ! $prop && $opt && $opt != -* ]] - then - prop=$opt - [[ $prop == @(${revProps// /|}) ]] && isRevProp=1 - last='prop' - continue - fi - - # property VALue - if [[ $isPsCmd && $prop && ! $val && $opt != -* ]] ; - then - val=$opt - last='val' - continue - fi - - if [[ $last != 'onlyarg' ]] - then - # more OPTions - case $opt in - -r|--revision|--revision=*) - hasRevisionOpt=1 - ;; - --revprop) - hasRevPropOpt=1 - # restrict to revision properties! - allProps=( $revProps ) - # on revprops, only one URL is expected - nExpectArgs=1 - ;; - -h|--help) - isHelpCmd=1 - ;; - -F|--file) - val='-F' - ;; - --relocate) - hasRelocateOpt=1 - ;; - --reintegrate) - hasReintegrateOpt=1 - ;; - esac - - # no more options, only arguments, whatever they look like. - if [[ $opt = '--' && ! $isCur ]] ; then - last='onlyarg' - continue - fi - - # options are recorded... - if [[ $opt == -* ]] ; then - # but not the current one! - [[ ! $isCur ]] && options="$options $opt " - last='opt' - continue - fi - else - # onlyarg - let nargs++ - continue - fi - - # then we have an argument - if [[ $cmd = 'merge' && ! $URL ]] ; then - # fist argument is the source URL for the merge - URL=$opt - fi - - last='arg' - let nargs++ - done - # end opt option processing... - [[ $stat ]] || stat=$last - - # suggest all subcommands, including special help - if [[ ! $cmd || $stat = 'cmd' ]] - then - COMPREPLY=( $( compgen -W "$cmds $specOpts" -- $cur ) ) - return 0 - fi - - # suggest all subcommands - if [[ $stat = 'help' || ( $isHelpCmd && ! $help ) ]] - then - COMPREPLY=( $( compgen -W "$cmds" -- $cur ) ) - return 0 - fi - - # URL completion - if [[ $cmd == @(co|checkout|ls|list) && $stat = 'arg' && \ - $SVN_BASH_COMPL_EXT == *urls* ]] - then - # see about COMP_WORDBREAKS workaround in prop completion - if [[ $cur == file:* ]] - then - # file completion for file:// urls - local where=${cur/file:/} - COMPREPLY=( $(compgen -d -S '/' -X '*/.*' -- $where ) ) - return - elif [[ $cur == *:* ]] - then - # get known urls - local urls= file= - for file in ~/.subversion/auth/svn.simple/* ; do - if [ -r $file ] ; then - local url=$(_svn_read_hashfile svn:realmstring < $file) - url=${url/**/} - urls="$urls $url" - fi - done - - # only suggest/show possible suffixes - local prefix=${cur%:*} suffix=${cur#*:} c= choices= - for c in $urls ; do - [[ $c == $prefix:* ]] && choices="$choices ${c#*:}" - done - - COMPREPLY=( $(compgen -W "$choices" -- $suffix ) ) - return - else - # show schemas - COMPREPLY=( $(compgen -W "$urlSchemas" -- $cur) ) - return - fi - fi - - if [[ $cmd = 'merge' || $cmd = 'mergeinfo' ]] - then - local here=$(_svn_info URL) - # suggest a possible URL for merging - if [[ ! $URL && $stat = 'arg' ]] ; then - # we assume a 'standard' repos with branches and trunk - if [[ "$here" == */branches/* ]] ; then - # we guess that it is a merge from the trunk - COMPREPLY=( $(compgen -W ${here/\/branches\/*/\/trunk} -- $cur ) ) - return 0 - elif [[ "$here" == */trunk* ]] ; then - # we guess that it is a merge from a branch - COMPREPLY=( $(compgen -W ${here/\/trunk*/\/branches\/} -- $cur ) ) - return 0 - else - # no se, let us suggest the repository root... - COMPREPLY=( $(compgen -W $(_svn_info Root) -- $cur ) ) - return 0 - fi - elif [[ $URL == */branches/* && $here == */trunk* && \ - ! $hasReintegrateOpt && $cur = '' && $stat = 'arg' ]] ; then - # force --reintegrate only if the current word is empty - COMPREPLY=( $(compgen -W '--reintegrate' -- $cur ) ) - return 0 - fi - fi - - # help about option arguments - if [[ $stat = 'skip' ]] - then - local previous=${COMP_WORDS[COMP_CWORD-1]} - local values= dirs= beep= exes= - - [[ $previous = '--config-dir' ]] && dirs=1 - - # external editor, diff, diff3... - [[ $previous = --*-cmd ]] && exes=1 - - [[ $previous = '--native-eol' ]] && values='LF CR CRLF' - - # just to suggest that a number is expected. hummm. - [[ $previous = '--limit' ]] && values='0 1 2 3 4 5 6 7 8 9' - - # some special partial help about --revision option. - [[ $previous = '--revision' || $previous = '-r' ]] && \ - values='HEAD BASE PREV COMMITTED 0 {' - - [[ $previous = '--encoding' ]] && \ - values="latin1 utf8 $SVN_BASH_ENCODINGS" - - [[ $previous = '--extensions' || $previous = '-x' ]] && \ - values="--unified --ignore-space-change \ - --ignore-all-space --ignore-eol-style --show-c-functions" - - [[ $previous = '--depth' ]] && \ - values='empty files immediates infinity' - - [[ $previous = '--set-depth' ]] && \ - values='empty exclude files immediates infinity' - - [[ $previous = '--accept' ]] && \ - { - # the list is different for 'resolve' - if [[ $cmd = 'resolve' ]] ; then - # from svn help resolve - values='base working mine-full theirs-full' - else # checkout merge switch update - values="postpone base mine-full theirs-full edit launch \ - mine-conflict theirs-conflict" - fi - } - - [[ $previous = '--show-revs' ]] && values='merged eligible' - - if [[ $previous = '--username' ]] ; then - values="$SVN_BASH_USERNAME" - if [[ $SVN_BASH_COMPL_EXT == *username* ]] ; then - local file= - # digest? others? - for file in ~/.subversion/auth/svn.simple/* ; do - if [ -r $file ] ; then - values="$values $(_svn_read_hashfile username < $file)" - fi - done - fi - [[ ! "$values" ]] && beep=1 - fi - - # could look at ~/.subversion/ ? - # hmmm... this option should not exist - [[ $previous = '--password' ]] && beep=1 - - # TODO: provide help about other options such as: - # --old --new --with-revprop - - # if the previous option required a parameter, do something - # or fallback on ordinary filename expansion - [[ $values ]] && COMPREPLY=( $( compgen -W "$values" -- $cur ) ) - [[ $dirs ]] && COMPREPLY=( $( compgen -o dirnames -- $cur ) ) - [[ $exes ]] && COMPREPLY=( $( compgen -c -- $cur ) ) - [[ $beep ]] && - { - # 'no known completion'. hummm. - echo -en "\a" - COMPREPLY=( '' ) - } - return 0 - fi - - # provide allowed property names after property commands - if [[ $isPropCmd && ( ! $prop || $stat = 'prop' ) && $cur != -* ]] - then - # - # Ok, this part is pretty ugly. - # - # The issue is that ":" is a completion word separator, - # which is a good idea for file:// urls but not within - # property names... - # - # The first idea was to remove locally ":" from COMP_WORDBREAKS - # and then put it back in all cases but in property name - # completion. It does not always work. There is a strange bug - # where one may get "svn:svn:xxx" in some unclear cases. - # - # Thus the handling is reprogrammed here... - # The code assumes that property names look like *:*, - # but it also works reasonably well with simple names. - # - # This hack is broken in bash4... not sure what to do about it, - # especially while keeping the bash3 compatibility:-( - local choices= - - if [[ $cur == *:* ]] - then - # only suggest/show possible suffixes - local prefix=${cur%:*} suffix=${cur#*:} c= - for c in ${allProps[@]} ; do - [[ $c == $prefix:* ]] && choices="$choices ${c#*:}" - done - # everything will be appended to the prefix because ':' is - # a separator, so cur is restricted to the suffix part. - cur=$suffix - else - # only one choice is fine - COMPREPLY=( $( compgen -W "${allProps[*]}" -- $cur ) ) - [ ${#COMPREPLY[@]} -eq 1 ] && return 0 - - # no ':' so only suggest prefixes? - local seen= n=0 last= c= - for c in ${allProps[@]%:*} ; do - # do not put the same prefix twice... - if [[ $c == $cur* && ( ! $seen || $c != @($seen) ) ]] - then - let n++ - last=$c - choices="$choices $c:" - if [[ $seen ]] - then - seen="$seen|$c*" - else - seen="$c*" - fi - fi - done - - # supply two choices to force a partial completion and a beep - [[ $n -eq 1 ]] && choices="$last:1 $last:2" - fi - - COMPREPLY=( $( compgen -W "$choices" -- $cur ) ) - return 0 - fi - - # force mandatory --revprop option on revision properties - if [[ $isRevProp && ! $hasRevPropOpt ]] - then - COMPREPLY=( $( compgen -W '--revprop' -- $cur ) ) - return 0 - fi - - # force mandatory --revision option on revision properties - if [[ $isRevProp && $hasRevPropOpt && ! $hasRevisionOpt ]] - then - COMPREPLY=( $( compgen -W '--revision' -- $cur ) ) - return 0 - fi - - # possible completion when setting property values - if [[ $isPsCmd && $prop && ( ! $val || $stat = 'val' ) ]] - then - # ' is a reminder for an arbitrary value - local values="\' --file" - case $prop in - svn:keywords) - # just a subset? - values="Id Rev URL Date Author Header \' $SVN_BASH_KEYWORDS" - ;; - svn:executable|svn:needs-lock) - # hmmm... canonical value * is special to the shell. - values='\\*' - ;; - svn:eol-style) - values='native LF CR CRLF' - ;; - svn:mime-type) - # could read /etc/mime.types if available. overkill. - values="text/ text/plain text/html text/xml text/rtf - image/ image/png image/gif image/jpeg image/tiff - audio/ audio/midi audio/mpeg - video/ video/mpeg video/mp4 - application/ application/octet-stream - $SVN_BASH_MIME_TYPE" - ;; - esac - - COMPREPLY=( $( compgen -W "$values" -- $cur ) ) - # special case for --file... return even if within an option - [[ ${COMPREPLY} ]] && return 0 - fi - - # maximum number of additional arguments expected in various forms - case $cmd in - merge) - nExpectArgs=3 - ;; - mergeinfo) - nExpectArgs=1 - ;; - copy|cp|move|mv|rename|ren|export|import) - nExpectArgs=2 - ;; - switch|sw) - [[ ! $hasRelocateOpt ]] && nExpectArgs=2 - ;; - help|h) - nExpectArgs=0 - ;; - --version) - nExpectArgs=0 - ;; - esac - - # the maximum number of arguments is reached for a command - if [[ $nExpectArgs && $nargs -gt $nExpectArgs ]] - then - # some way to tell 'no completion at all'... is there a better one? - # Do not say 'file completion' here. - echo -en "\a" - COMPREPLY=( '' ) - return 0 - fi - - # if not typing an option, - # then fallback on filename expansion... - if [[ $cur != -* || $stat = 'onlyarg' ]] ; then - - # do we allow possible expensive completion here? - if [[ $SVN_BASH_COMPL_EXT == *svnstatus* ]] ; then - - # build status command and options - # "--quiet" removes 'unknown' files - local status='svn status --non-interactive' - - [[ $SVN_BASH_COMPL_EXT == *recurse* ]] || \ - status="$status --non-recursive" - - # I'm not sure that it can work with externals in call cases - # the output contains translatable sentences (even with quiet) - [[ $SVN_BASH_COMPL_EXT == *externals* ]] || \ - status="$status --ignore-externals" - - local cs= files= - # subtlety: must not set $cur* if $cur is empty in some cases - [[ $cur ]] && cs=$cur* - - # 'files' is set according to the current subcommand - case $cmd in - st*) # status completion must include all files - files=$cur* - ;; - ci|commit|revert|di*) # anything edited - files=$($status $cs| _svn_grcut '@([MADR!]*| M*|_M*)') - ;; - add) # unknown files - files=$($status $cs| _svn_grcut '\?*') - ;; - unlock) # unlock locked files - files=$($status $cs| _svn_grcut '@(??L*|?????[KOTB]*)') - ;; - resolve*) # files in conflict - files=$($status $cs| _svn_grcut '@(?C*|C*)') - ;; - praise|blame|ann*) # any svn file but added - files=$( _svn_lls all $cur* ) - ;; - p*) # prop commands - if [[ $cmd == @($propCmds) && \ - $prop == @(svn:ignore|svn:externals) ]] ; then - # directory specific props - files=$( _svn_lls dir . $cur* ) - else - # ??? added directories appear twice: foo foo/ - files="$( _svn_lls all $cur* ) - $($status $cs | _svn_grcut 'A*' )" - fi - ;; - info) # information on any file - files="$( _svn_lls all $cur* ) - $($status $cs | _svn_grcut 'A*' )" - ;; - remove|rm|del*|move|mv|rename) # changing existing files - files=$( _svn_lls all $cur* ) - ;; - mkdir) # completion in mkdir can only be for subdirs? - files=$( _svn_lls dir $cur* ) - ;; - log|lock|up*|cl*|switch) # misc, all but added files - files=$( _svn_lls all $cur* ) - ;; - merge) # may do a better job? URL/WCPATH - files=$( _svn_lls all $cur* ) - ;; - ls|list) # better job? what about URLs? - files=$( _svn_lls all $cur* ) - ;; - *) # other commands: changelist export import cat mergeinfo - local fallback=1 - ;; - esac - - # when not recursive, some relevant files may exist - # within subdirectories, so they are added here. - # should it be restricted to svn-managed subdirs? no?? - if [[ $SVN_BASH_COMPL_EXT != *recurse* ]] ; then - files="$files $( _svn_lls dir $cur* )" - fi - - # set completion depending on computed 'files' - if [[ $files ]] ; then - COMPREPLY=( $( compgen -W "$files" -- $cur ) ) - # if empty, set to nope? - [[ "${COMPREPLY[*]}" ]] || COMPREPLY=( '' ) - elif [[ ! $fallback ]] ; then - # this suggests no completion... - echo -en "\a" - COMPREPLY=( '' ) - fi - fi - # else fallback to ordinary filename completion... - return 0 - fi - - # otherwise build possible options for the command - pOpts="--username --password --no-auth-cache --non-interactive \ - --trust-server-cert --force-interactive" - mOpts="-m --message -F --file --encoding --force-log --with-revprop" - rOpts="-r --revision" - qOpts="-q --quiet" - nOpts="-N --non-recursive --depth" - gOpts="-g --use-merge-history" - cOpts="--cl --changelist" - - cmdOpts= - case $cmd in - --version) - cmdOpts="$qOpts" - ;; - add) - cmdOpts="--auto-props --no-auto-props --force --targets \ - --no-ignore --parents $nOpts $qOpts $pOpts" - ;; - blame|annotate|ann|praise) - cmdOpts="$rOpts $pOpts -v --verbose --incremental --xml \ - -x --extensions --force $gOpts" - ;; - cat) - cmdOpts="$rOpts $pOpts" - ;; - changelist|cl) - cmdOpts="--targets $pOpts $qOpts $cOpts \ - -R --recursive --depth --remove" - ;; - checkout|co) - cmdOpts="$rOpts $qOpts $nOpts $pOpts --ignore-externals \ - --force" - ;; - cleanup) - cmdOpts="--diff3-cmd $pOpts" - ;; - commit|ci) - cmdOpts="$mOpts $qOpts $nOpts --targets --editor-cmd $pOpts \ - --no-unlock $cOpts --keep-changelists \ - --include-externals" - ;; - copy|cp) - cmdOpts="$mOpts $rOpts $qOpts --editor-cmd $pOpts --parents \ - --ignore-externals" - ;; - delete|del|remove|rm) - cmdOpts="--force $mOpts $qOpts --targets --editor-cmd $pOpts \ - --keep-local" - ;; - diff|di) - cmdOpts="$rOpts -x --extensions --diff-cmd --no-diff-deleted \ - $nOpts $pOpts --force --old --new --notice-ancestry \ - -c --change --summarize $cOpts --xml --git \ - --internal-diff --show-copies-as-adds \ - --ignore-properties --properties-only --no-diff-added \ - --patch-compatible" - ;; - export) - cmdOpts="$rOpts $qOpts $pOpts $nOpts --force --native-eol \ - --ignore-externals --ignore-keywords" - ;; - help|h|\?) - cmdOpts= - ;; - import) - cmdOpts="--auto-props --no-auto-props $mOpts $qOpts $nOpts \ - --no-ignore --editor-cmd $pOpts --force" - ;; - info) - cmdOpts="$pOpts $rOpts --targets -R --recursive --depth \ - --incremental --xml $cOpts" - ;; - list|ls) - cmdOpts="$rOpts -v --verbose -R --recursive $pOpts \ - --incremental --xml --depth --include-externals" - ;; - lock) - cmdOpts="-m --message -F --file --encoding --force-log \ - --targets --force $pOpts" - ;; - log) - cmdOpts="$rOpts -v --verbose --targets $pOpts --stop-on-copy \ - --incremental --xml $qOpts -l --limit -c --change \ - $gOpts --with-all-revprops --with-revprop --depth \ - --diff --diff-cmd -x --extensions --internal-diff \ - --with-no-revprops --search --search-and" - ;; - merge) - cmdOpts="$rOpts $nOpts $qOpts --force --dry-run --diff3-cmd \ - $pOpts --ignore-ancestry -c --change -x --extensions \ - --record-only --accept --reintegrate \ - --allow-mixed-revisions -v --verbose" - ;; - mergeinfo) - cmdOpts="$rOpts $pOpts --depth --show-revs -R --recursive" - ;; - mkdir) - cmdOpts="$mOpts $qOpts --editor-cmd $pOpts --parents" - ;; - move|mv|rename|ren) - cmdOpts="$mOpts $rOpts $qOpts --force --editor-cmd $pOpts \ - --parents --allow-mixed-revisions" - ;; - patch) - cmdOpts="$qOpts $pOpts --dry-run --ignore-whitespace \ - --reverse-diff --strip" - ;; - propdel|pdel|pd) - cmdOpts="$qOpts -R --recursive $rOpts $pOpts $cOpts \ - --depth" - [[ $isRevProp || ! $prop ]] && cmdOpts="$cmdOpts --revprop" - ;; - propedit|pedit|pe) - cmdOpts="--editor-cmd $pOpts $mOpts --force" - [[ $isRevProp || ! $prop ]] && \ - cmdOpts="$cmdOpts --revprop $rOpts" - ;; - propget|pget|pg) - cmdOpts="-v --verbose -R --recursive $rOpts --strict \ - $pOpts $cOpts --depth --xml --show-inherited-props" - [[ $isRevProp || ! $prop ]] && cmdOpts="$cmdOpts --revprop" - ;; - proplist|plist|pl) - cmdOpts="-v --verbose -R --recursive $rOpts --revprop $qOpts \ - $pOpts $cOpts --depth --xml --show-inherited-props" - ;; - propset|pset|ps) - cmdOpts="$qOpts --targets -R --recursive \ - --encoding $pOpts --force $cOpts --depth" - [[ $isRevProp || ! $prop ]] && \ - cmdOpts="$cmdOpts --revprop $rOpts" - [[ $val ]] || cmdOpts="$cmdOpts -F --file" - ;; - relocate) - cmdOpts="--ignore-externals $pOpts" - ;; - resolve) - cmdOpts="--targets -R --recursive $qOpts $pOpts --accept \ - --depth" - ;; - resolved) - cmdOpts="--targets -R --recursive $qOpts $pOpts --depth" - ;; - revert) - cmdOpts="--targets -R --recursive $qOpts $cOpts \ - --depth $pOpts" - ;; - status|stat|st) - cmdOpts="-u --show-updates -v --verbose $nOpts $qOpts $pOpts \ - --no-ignore --ignore-externals --incremental --xml \ - $cOpts" - ;; - switch|sw) - cmdOpts="--relocate $rOpts $nOpts $qOpts $pOpts --diff3-cmd \ - --force --accept --ignore-externals --set-depth \ - --ignore-ancestry" - ;; - unlock) - cmdOpts="--targets --force $pOpts" - ;; - update|up) - cmdOpts="$rOpts $nOpts $qOpts $pOpts --diff3-cmd \ - --ignore-externals --force --accept $cOpts \ - --parents --editor-cmd --set-depth" - ;; - upgrade) - cmdOpts="$qOpts $pOpts" - ;; - *) - ;; - esac - - # add options that are nearly always available - [[ "$cmd" != "--version" ]] && cmdOpts="$cmdOpts $helpOpts" - cmdOpts="$cmdOpts --config-dir --config-option" - - # --accept (edit|launch) incompatible with --non-interactive - if [[ $acceptOpt == @(edit|launch) ]] ; - then - cmdOpts=${cmdOpts/ --non-interactive / } - fi - - # take out options already given - for opt in $options - do - local optBase - - # remove leading dashes and arguments - case $opt in - --*) optBase=${opt/=*/} ;; - -*) optBase=${opt:0:2} ;; - esac - - cmdOpts=" $cmdOpts " - cmdOpts=${cmdOpts/ ${optBase} / } - - # take out alternatives and mutually exclusives - case $optBase in - -v) cmdOpts=${cmdOpts/ --verbose / } ;; - --verbose) cmdOpts=${cmdOpts/ -v / } ;; - -N) cmdOpts=${cmdOpts/ --non-recursive / } ;; - --non-recursive) cmdOpts=${cmdOpts/ -N / } ;; - -R) cmdOpts=${cmdOpts/ --recursive / } ;; - --recursive) cmdOpts=${cmdOpts/ -R / } ;; - -x) cmdOpts=${cmdOpts/ --extensions / } ;; - --extensions) cmdOpts=${cmdOpts/ -x / } ;; - -q) cmdOpts=${cmdOpts/ --quiet / } ;; - --quiet) cmdOpts=${cmdOpts/ -q / } ;; - -h) cmdOpts=${cmdOpts/ --help / } ;; - --help) cmdOpts=${cmdOpts/ -h / } ;; - -l) cmdOpts=${cmdOpts/ --limit / } ;; - --limit) cmdOpts=${cmdOpts/ -l / } ;; - -r) cmdOpts=${cmdOpts/ --revision / } ;; - --revision) cmdOpts=${cmdOpts/ -r / } ;; - -c) cmdOpts=${cmdOpts/ --change / } ;; - --change) cmdOpts=${cmdOpts/ -c / } ;; - --auto-props) cmdOpts=${cmdOpts/ --no-auto-props / } ;; - --no-auto-props) cmdOpts=${cmdOpts/ --auto-props / } ;; - -g) cmdOpts=${cmdOpts/ --use-merge-history / } ;; - --use-merge-history) - cmdOpts=${cmdOpts/ -g / } ;; - -m|--message|-F|--file) - cmdOpts=${cmdOpts/ --message / } - cmdOpts=${cmdOpts/ -m / } - cmdOpts=${cmdOpts/ --file / } - cmdOpts=${cmdOpts/ -F / } - ;; - esac - - # remove help options within help subcommand - if [ $isHelpCmd ] ; then - cmdOpts=${cmdOpts/ -h / } - cmdOpts=${cmdOpts/ --help / } - fi - done - - # provide help about available options - COMPREPLY=( $( compgen -W "$cmdOpts" -- $cur ) ) +# Don't handle completion if it's already managed +if _completion_exists svn; then + _log_warning "completion already loaded - this usually means it is safe to stop using this completion" return 0 -} -complete -F _svn -o default -X '@(*/.svn|*/.svn/|.svn|.svn/)' svn +fi -_svnadmin () -{ - local cur cmds cmdOpts optsParam opt helpCmds optBase i +_svn_bash_completion_xcrun_svn= +if _command_exists xcrun; then + _svn_bash_completion_xcrun_svn="$(xcrun --find svn)" +fi +_svn_bash_completion_paths=( + # Standard locations + "${SVN_EXE%/*}/../etc/bash_completion.d/subversion" + # MacOS non-system locations + "${_svn_bash_completion_xcrun_svn%/bin/svn}/etc/bash_completion.d/subversion" +) - COMPREPLY=() - cur=${COMP_WORDS[COMP_CWORD]} - - # Possible expansions, without pure-prefix abbreviations such as "h". - cmds='crashtest create deltify dump freeze help hotcopy list-dblogs \ - list-unused-dblogs load lock lslocks lstxns pack recover rmlocks \ - rmtxns setlog setrevprop setuuid unlock upgrade verify --version' - - if [[ $COMP_CWORD -eq 1 ]] ; then - COMPREPLY=( $( compgen -W "$cmds" -- $cur ) ) - return 0 +# Load the first completion file found +_svn_bash_completion_found=false +for _comp_path in "${_svn_bash_completion_paths[@]}"; do + if [[ -r "$_comp_path" ]]; then + _svn_bash_completion_found=true + # shellcheck disable=SC1090 # don't follow + source "$_comp_path" + break fi +done - # options that require a parameter - # note: continued lines must end '|' continuing lines must start '|' - optsParam="-r|--revision|--parent-dir|--fs-type|-M|--memory-cache-size" - optsParam="$optsParam|-F|--file" - - # if not typing an option, or if the previous option required a - # parameter, then fallback on ordinary filename expansion - helpCmds='help|--help|h|\?' - if [[ ${COMP_WORDS[1]} != @($helpCmds) ]] && \ - [[ "$cur" != -* ]] || \ - [[ ${COMP_WORDS[COMP_CWORD-1]} == @($optsParam) ]] ; then - return 0 - fi - - cmdOpts= - case ${COMP_WORDS[1]} in - create) - cmdOpts="--bdb-txn-nosync --bdb-log-keep --config-dir \ - --fs-type --pre-1.4-compatible --pre-1.5-compatible \ - --pre-1.6-compatible --compatible-version" - ;; - deltify) - cmdOpts="-r --revision -q --quiet" - ;; - dump) - cmdOpts="-r --revision --incremental -q --quiet --deltas \ - -M --memory-cache-size" - ;; - freeze) - cmdOpts="-F --file" - ;; - help|h|\?) - cmdOpts="$cmds" - ;; - hotcopy) - cmdOpts="--clean-logs" - ;; - load) - cmdOpts="--ignore-uuid --force-uuid --parent-dir -q --quiet \ - --use-pre-commit-hook --use-post-commit-hook \ - --bypass-prop-validation -M --memory-cache-size" - ;; - lock|unlock) - cmdOpts="--bypass-hooks" - ;; - recover) - cmdOpts="--wait" - ;; - rmtxns) - cmdOpts="-q --quiet" - ;; - setlog) - cmdOpts="-r --revision --bypass-hooks" - ;; - setrevprop) - cmdOpts="-r --revision --use-pre-revprop-change-hook \ - --use-post-revprop-change-hook" - ;; - verify) - cmdOpts="-r --revision -q --quiet" - ;; - *) - ;; - esac - - cmdOpts="$cmdOpts --help -h" - - # take out options already given - for (( i=2; i<=$COMP_CWORD-1; ++i )) ; do - opt=${COMP_WORDS[$i]} - - case $opt in - --*) optBase=${opt/=*/} ;; - -*) optBase=${opt:0:2} ;; - esac - - cmdOpts=" $cmdOpts " - cmdOpts=${cmdOpts/ ${optBase} / } - - # take out alternatives - case $optBase in - -q) cmdOpts=${cmdOpts/ --quiet / } ;; - --quiet) cmdOpts=${cmdOpts/ -q / } ;; - -h) cmdOpts=${cmdOpts/ --help / } ;; - --help) cmdOpts=${cmdOpts/ -h / } ;; - -r) cmdOpts=${cmdOpts/ --revision / } ;; - --revision) cmdOpts=${cmdOpts/ -r / } ;; - -F) cmdOpts=${cmdOpts/ --file / } ;; - --file) cmdOpts=${cmdOpts/ -F / } ;; - -M) cmdOpts=${cmdOpts/ --memory-cache-size / } ;; - --memory-cache-size) cmdOpts=${cmdOpts/ --M / } ;; - esac - - # skip next option if this one requires a parameter - if [[ $opt == @($optsParam) ]] ; then - ((++i)) - fi - done - - COMPREPLY=( $( compgen -W "$cmdOpts" -- $cur ) ) - - return 0 -} -complete -F _svnadmin -o default svnadmin - -_svndumpfilter () -{ - local cur cmds cmdOpts optsParam opt helpCmds optBase i - - COMPREPLY=() - cur=${COMP_WORDS[COMP_CWORD]} - - # Possible expansions, without pure-prefix abbreviations such as "h". - cmds='exclude help include --version' - - if [[ $COMP_CWORD -eq 1 ]] ; then - COMPREPLY=( $( compgen -W "$cmds" -- $cur ) ) - return 0 - fi - - # options that require a parameter - # note: continued lines must end '|' continuing lines must start '|' - optsParam="--targets" - - # if not typing an option, or if the previous option required a - # parameter, then fallback on ordinary filename expansion - helpCmds='help|--help|h|\?' - if [[ ${COMP_WORDS[1]} != @($helpCmds) ]] && \ - [[ "$cur" != -* ]] || \ - [[ ${COMP_WORDS[COMP_CWORD-1]} == @($optsParam) ]] ; then - return 0 - fi - - cmdOpts= - case ${COMP_WORDS[1]} in - exclude|include) - cmdOpts="--drop-empty-revs --renumber-revs - --skip-missing-merge-sources --targets - --preserve-revprops --quiet" - ;; - help|h|\?) - cmdOpts="$cmds" - ;; - *) - ;; - esac - - cmdOpts="$cmdOpts --help -h" - - # take out options already given - for (( i=2; i<=$COMP_CWORD-1; ++i )) ; do - opt=${COMP_WORDS[$i]} - - case $opt in - --*) optBase=${opt/=*/} ;; - -*) optBase=${opt:0:2} ;; - esac - - cmdOpts=" $cmdOpts " - cmdOpts=${cmdOpts/ ${optBase} / } - - # take out alternatives - case $optBase in - -h) cmdOpts=${cmdOpts/ --help / } ;; - --help) cmdOpts=${cmdOpts/ -h / } ;; - esac - - # skip next option if this one requires a parameter - if [[ $opt == @($optsParam) ]] ; then - ((++i)) - fi - done - - COMPREPLY=( $( compgen -W "$cmdOpts" -- $cur ) ) - - return 0 -} -complete -F _svndumpfilter -o default svndumpfilter - -_svnlook () -{ - local cur cmds cmdOpts optsParam opt helpCmds optBase i - - COMPREPLY=() - cur=${COMP_WORDS[COMP_CWORD]} - - # Possible expansions, without pure-prefix abbreviations such as "h". - cmds='author cat changed date diff dirs-changed help history info \ - lock log propget proplist tree uuid youngest --version' - - if [[ $COMP_CWORD -eq 1 ]] ; then - COMPREPLY=( $( compgen -W "$cmds" -- $cur ) ) - return 0 - fi - - # options that require a parameter - # note: continued lines must end '|' continuing lines must start '|' - optsParam="-r|--revision|-t|--transaction|-l|--limit|-x|--extensions" - - # if not typing an option, or if the previous option required a - # parameter, then fallback on ordinary filename expansion - helpCmds='help|--help|h|\?' - if [[ ${COMP_WORDS[1]} != @($helpCmds) ]] && \ - [[ "$cur" != -* ]] || \ - [[ ${COMP_WORDS[COMP_CWORD-1]} == @($optsParam) ]] ; then - return 0 - fi - - cmdOpts= - case ${COMP_WORDS[1]} in - author) - cmdOpts="-r --revision -t --transaction" - ;; - cat) - cmdOpts="-r --revision -t --transaction" - ;; - changed) - cmdOpts="-r --revision -t --transaction --copy-info" - ;; - date) - cmdOpts="-r --revision -t --transaction" - ;; - diff) - cmdOpts="-r --revision -t --transaction --diff-copy-from \ - --no-diff-added --no-diff-deleted -x --extensions" - ;; - dirs-changed) - cmdOpts="-r --revision -t --transaction" - ;; - help|h|\?) - cmdOpts="$cmds" - ;; - history) - cmdOpts="-r --revision -l --limit --show-ids" - ;; - info) - cmdOpts="-r --revision -t --transaction" - ;; - lock) - cmdOpts= - ;; - log) - cmdOpts="-r --revision -t --transaction" - ;; - propget|pget|pg) - cmdOpts="-r --revision -t --transaction --revprop" - ;; - proplist|plist|pl) - cmdOpts="-r --revision -t --transaction --revprop -v --verbose --xml" - ;; - tree) - cmdOpts="-r --revision -t --transaction --full-paths -N --non-recursive --show-ids" - ;; - uuid) - cmdOpts= - ;; - youngest) - cmdOpts= - ;; - *) - ;; - esac - - cmdOpts="$cmdOpts --help -h" - - # take out options already given - for (( i=2; i<=$COMP_CWORD-1; ++i )) ; do - opt=${COMP_WORDS[$i]} - - case $opt in - --*) optBase=${opt/=*/} ;; - -*) optBase=${opt:0:2} ;; - esac - - cmdOpts=" $cmdOpts " - cmdOpts=${cmdOpts/ ${optBase} / } - - # take out alternatives - case $optBase in - -N) cmdOpts=${cmdOpts/ --non-recursive / } ;; - --non-recursive) cmdOpts=${cmdOpts/ -N / } ;; - -h) cmdOpts=${cmdOpts/ --help / } ;; - --help) cmdOpts=${cmdOpts/ -h / } ;; - -l) cmdOpts=${cmdOpts/ --limit / } ;; - --limit) cmdOpts=${cmdOpts/ -l / } ;; - -r) cmdOpts=${cmdOpts/ --revision / } ;; - --revision) cmdOpts=${cmdOpts/ -r / } ;; - -t) cmdOpts=${cmdOpts/ --transaction / } ;; - --transaction) cmdOpts=${cmdOpts/ -t / } ;; - -v) cmdOpts=${cmdOpts/ --verbose / } ;; - --verbose) cmdOpts=${cmdOpts/ -v / } ;; - -x) cmdOpts=${cmdOpts/ --extensions / } ;; - --extensions) cmdOpts=${cmdOpts/ -x / } ;; - esac - - # skip next option if this one requires a parameter - if [[ $opt == @($optsParam) ]] ; then - ((++i)) - fi - done - - COMPREPLY=( $( compgen -W "$cmdOpts" -- $cur ) ) - - return 0 -} -complete -F _svnlook -o default svnlook - -_svnsync () -{ - local cur cmds cmdOpts optsParam opt helpCmds optBase i - - COMPREPLY=() - cur=${COMP_WORDS[COMP_CWORD]} - - # Possible expansions, without pure-prefix abbreviations such as "h". - cmds='copy-revprops help info initialize synchronize --version' - - if [[ $COMP_CWORD -eq 1 ]] ; then - COMPREPLY=( $( compgen -W "$cmds" -- $cur ) ) - return 0 - fi - - # options that require a parameter - # note: continued lines must end '|' continuing lines must start '|' - optsParam="--config-dir|--config-option|--source-username|--source-password" - optsParam="$optsParam|--sync-username|--sync-password" - - # if not typing an option, or if the previous option required a - # parameter, then fallback on ordinary filename expansion - helpCmds='help|--help|h|\?' - if [[ ${COMP_WORDS[1]} != @($helpCmds) ]] && \ - [[ "$cur" != -* ]] || \ - [[ ${COMP_WORDS[COMP_CWORD-1]} == @($optsParam) ]] ; then - return 0 - fi - - cmdOpts= - case ${COMP_WORDS[1]} in - copy-revprops|initialize|init|synchronize|sync) - cmdOpts="--non-interactive --no-auth-cache --trust-server-cert \ - --source-username --source-password --sync-username \ - --sync-password --config-dir --config-option -q --quiet" - ;; - help|h|\?) - cmdOpts="$cmds" - ;; - info) - cmdOpts="--non-interactive --no-auth-cache --trust-server-cert \ - --source-username --source-password --sync-username \ - --sync-password --config-dir --config-option" - ;; - *) - ;; - esac - - cmdOpts="$cmdOpts --help -h" - - # take out options already given - for (( i=2; i<=$COMP_CWORD-1; ++i )) ; do - opt=${COMP_WORDS[$i]} - - case $opt in - --*) optBase=${opt/=*/} ;; - -*) optBase=${opt:0:2} ;; - esac - - cmdOpts=" $cmdOpts " - cmdOpts=${cmdOpts/ ${optBase} / } - - # take out alternatives - case $optBase in - -h) cmdOpts=${cmdOpts/ --help / } ;; - --help) cmdOpts=${cmdOpts/ -h / } ;; - -q) cmdOpts=${cmdOpts/ --quiet / } ;; - --quiet) cmdOpts=${cmdOpts/ -q / } ;; - esac - - # skip next option if this one requires a parameter - if [[ $opt == @($optsParam) ]] ; then - ((++i)) - fi - done - - COMPREPLY=( $( compgen -W "$cmdOpts" -- $cur ) ) - - return 0 -} -complete -F _svnsync -o default svnsync - -# reasonable completion for 'svnversion' -_svnversion () -{ - local cmdOpts=" -n --no-newline -c --committed -h --help --version " - local cur=${COMP_WORDS[COMP_CWORD]} - - COMPREPLY=() - - # parse current options - local options= wcpath= trailurl= last='none' stat= opt= i=-1 isCur= - for opt in ${COMP_WORDS[@]} - do - [[ $i -eq $COMP_CWORD ]] && stat=$last - let i++ - - # are we processing the current word? - isCur= - [[ $i -eq $COMP_CWORD ]] && isCur=1 - - # skip first command, should be 'svnversion' - if [ $last = 'none' ] ; then - last='first' - continue - fi - - # get options - if [[ $last != 'arg' && $opt == -* ]] - then - # if '--' is at the current position, it means that we are looking - # for '--*' options, and not the end of option processing. - if [[ $opt = '--' && ! $isCur ]] - then - last='arg' - else - options="$options $opt " - last='opt' - fi - continue - fi - # get arguments - if [[ $opt != -* ]] - then - last='arg' - if [[ ! $wcpath ]] - then - wcpath=$opt - elif [[ ! $trailurl ]] - then - trailurl=$opt - fi - fi - done - [[ $stat ]] || stat=$last - - # argument part - if [[ $cur != -* || $stat = 'arg' ]] - then - [[ $wcpath && $trailurl ]] && COMPREPLY=( '' ) - return 0 - fi - - # suggest options, and take out already given options - for opt in $options - do - # take out options - cmdOpts=${cmdOpts/ $opt / } - - # take out alternatives - case $opt in - -n) cmdOpts=${cmdOpts/ --no-newline / } ;; - --no-newline) cmdOpts=${cmdOpts/ -n / } ;; - -h) cmdOpts=${cmdOpts/ --help / } ;; - --help) cmdOpts=${cmdOpts/ -h / } ;; - -c) cmdOpts=${cmdOpts/ --committed / } ;; - --committed) cmdOpts=${cmdOpts/ -c / } ;; - esac - done - - COMPREPLY=( $( compgen -W "$cmdOpts" -- $cur ) ) - - return 0 -} -# -X option does not seem to work? -complete -F _svnversion -o dirnames -X '*.svn*' svnversion +# Cleanup +if [[ "${_svn_bash_completion_found}" == false ]]; then + _log_warning "no completion files found - please try enabling the 'system' completion instead." +fi +unset "${!_svn_bash_completion@}" diff --git a/completion/available/system.completion.bash b/completion/available/system.completion.bash old mode 100755 new mode 100644 index 113a93f5..bb1d14eb --- a/completion/available/system.completion.bash +++ b/completion/available/system.completion.bash @@ -1,59 +1,52 @@ -#!/usr/bin/env bash +# shellcheck shell=bash # # Loads the system's Bash completion modules. # If Homebrew is installed (OS X), it's Bash completion modules are loaded. -if shopt -qo nounset -then # Bash-completion is too large and complex to expect to handle unbound variables throughout the whole codebase. +# Load before other completions +# BASH_IT_LOAD_PRIORITY: 325 + +# Bash-completion is too large and complex to expect to handle unbound variables throughout the whole codebase. +if shopt -qo nounset; then __bash_it_restore_nounset=true shopt -uo nounset else __bash_it_restore_nounset=false fi -if [[ -r "${BASH_COMPLETION:-}" ]] ; then - # shellcheck disable=SC1091 +# shellcheck disable=SC1090 disable=SC1091 +if [[ -r "${BASH_COMPLETION:-}" ]]; then source "${BASH_COMPLETION}" - -elif [[ -r /etc/bash_completion ]] ; then - # shellcheck disable=SC1091 - source /etc/bash_completion - +elif [[ -r /etc/bash_completion ]]; then + source /etc/bash_completion # Some distribution makes use of a profile.d script to import completion. -elif [[ -r /etc/profile.d/bash_completion.sh ]] ; then - # shellcheck disable=SC1091 - source /etc/profile.d/bash_completion.sh - -elif _bash_it_homebrew_check -then - : ${BASH_COMPLETION_COMPAT_DIR:=$BASH_IT_HOMEBREW_PREFIX/etc/bash_completion.d} - - case "${BASH_VERSION}" in - 1*|2*|3.0*|3.1*) - _log_warning "Cannot load completion due to version of shell. Are you using Bash 3.2+?" - ;; - 3.2*|4.0*|4.1*) - # Import version 1.x of bash-completion, if installed. - BASH_COMPLETION="$BASH_IT_HOMEBREW_PREFIX/opt/bash-completion@1/etc/bash_completion" - if [[ -r "$BASH_COMPLETION" ]] ; then - # shellcheck disable=SC1090 - source "$BASH_COMPLETION" - else - unset BASH_COMPLETION - fi - ;; - 4.2*|5*|*) - # homebrew/versions/bash-completion2 (required for projects.completion.bash) is installed to this path - if [[ -r "$BASH_IT_HOMEBREW_PREFIX"/etc/profile.d/bash_completion.sh ]] ; then - # shellcheck disable=SC1090 - source "$BASH_IT_HOMEBREW_PREFIX"/etc/profile.d/bash_completion.sh - fi - ;; - esac +elif [[ -r /etc/profile.d/bash_completion.sh ]]; then + source /etc/profile.d/bash_completion.sh +elif _bash_it_homebrew_check; then + : "${BASH_COMPLETION_COMPAT_DIR:=${BASH_IT_HOMEBREW_PREFIX}/etc/bash_completion.d}" + case "${BASH_VERSION}" in + 1* | 2* | 3.0* | 3.1*) + _log_warning "Cannot load completion due to version of shell. Are you using Bash 3.2+?" + ;; + 3.2* | 4.0* | 4.1*) + # Import version 1.x of bash-completion, if installed. + BASH_COMPLETION="${BASH_IT_HOMEBREW_PREFIX}/opt/bash-completion@1/etc/bash_completion" + if [[ -r "$BASH_COMPLETION" ]]; then + source "$BASH_COMPLETION" + else + unset BASH_COMPLETION + fi + ;; + 4.2* | 5* | *) + # homebrew/versions/bash-completion2 (required for projects.completion.bash) is installed to this path + if [[ -r "${BASH_IT_HOMEBREW_PREFIX}/opt/bash-completion@2/etc/profile.d/bash_completion.sh" ]]; then + source "${BASH_IT_HOMEBREW_PREFIX}/opt/bash-completion@2/etc/profile.d/bash_completion.sh" + fi + ;; + esac fi -if $__bash_it_restore_nounset -then +if [[ ${__bash_it_restore_nounset:-false} == "true" ]]; then shopt -so nounset fi unset __bash_it_restore_nounset diff --git a/completion/available/travis.completion.bash b/completion/available/travis.completion.bash index 28d599aa..49d8f2cc 100644 --- a/completion/available/travis.completion.bash +++ b/completion/available/travis.completion.bash @@ -1,5 +1,10 @@ -if [[ -x "$(which travis)" ]]; then - __TRAVIS_COMPLETION_SCRIPT="${TRAVIS_CONFIG_PATH:-${HOME}/.travis}/travis.sh" - [[ -f "${__TRAVIS_COMPLETION_SCRIPT}" ]] && source "${__TRAVIS_COMPLETION_SCRIPT}" +# shellcheck shell=bash + +if _command_exists travis +then + if [[ -s "${__TRAVIS_COMPLETION_SCRIPT:=${TRAVIS_CONFIG_PATH:-${HOME}/.travis}/travis.sh}" ]] + then + source "${__TRAVIS_COMPLETION_SCRIPT}" + fi unset __TRAVIS_COMPLETION_SCRIPT fi diff --git a/completion/available/vuejs.completion.bash b/completion/available/vuejs.completion.bash index bbd79b9c..751658f0 100644 --- a/completion/available/vuejs.completion.bash +++ b/completion/available/vuejs.completion.bash @@ -1,7 +1,9 @@ # shellcheck shell=bash __vuejs_completion() { + # shellcheck disable=SC2155 local prev=$(_get_pword) + # shellcheck disable=SC2155 local curr=$(_get_cword) case $prev in diff --git a/completion/available/wpscan.completion.bash b/completion/available/wpscan.completion.bash index 5d2e2daa..105468a3 100644 --- a/completion/available/wpscan.completion.bash +++ b/completion/available/wpscan.completion.bash @@ -1,15 +1,16 @@ # shellcheck shell=bash -if command -v wpscan > /dev/null; then - __wpscan_completion() { - local OPTS=("--help --hh --version --url --ignore-main-redirect --verbose --output --format --detection-mode --scope --headers --user-agent --vhost --random-user-agent --user-agents-list --http-auth --max-threads --throttle --request-timeout --connect-timeout --disable-tlc-checks --proxy --proxy-auth --cookie-string --cookie-jar --cache-ttl --clear-cache --server --cache-dir --update --no-update --wp-content-dir --wp-plugins-dir --wp-version-detection --main-theme-detection --enumerate --exclude-content-based --plugins-list --plugins-detection --plugins-version-all --plugins-version-detection --themes-list --themes-detection --themes-version-all --themes-version-detection --timthumbs-list --timthumbs-detection --config-backups-list --config-backups-detection --db-exports-list --db-exports-detection --medias-detection --users-list --users-detection --passwords --usernames --multicall-max-passwords --password-attack --stealthy") - COMPREPLY=() - for _opt_ in "${OPTS[@]}"; do - if [[ "$_opt_" == "$2"* ]]; then - COMPREPLY+=("$_opt_") - fi - done - } +_command_exists wpscan || return - complete -F __wpscan_completion wpscan -fi +function __wpscan_completion() { + local _opt_ + local OPTS=('--help' '--hh' '--version' '--url' '--ignore-main-redirect' '--verbose' '--output' '--format' '--detection-mode' '--scope' '--headers' '--user-agent' '--vhost' '--random-user-agent' '--user-agents-list' '--http-auth' '--max-threads' '--throttle' '--request-timeout' '--connect-timeout' '--disable-tlc-checks' '--proxy' '--proxy-auth' '--cookie-string' '--cookie-jar' '--cache-ttl' '--clear-cache' '--server' '--cache-dir' '--update' '--no-update' '--wp-content-dir' '--wp-plugins-dir' '--wp-version-detection' '--main-theme-detection' '--enumerate' '--exclude-content-based' '--plugins-list' '--plugins-detection' '--plugins-version-all' '--plugins-version-detection' '--themes-list' '--themes-detection' '--themes-version-all' '--themes-version-detection' '--timthumbs-list' '--timthumbs-detection' '--config-backups-list' '--config-backups-detection' '--db-exports-list' '--db-exports-detection' '--medias-detection' '--users-list' '--users-detection' '--passwords' '--usernames' '--multicall-max-passwords' '--password-attack' '--stealthy') + COMPREPLY=() + for _opt_ in "${OPTS[@]}"; do + if [[ "$_opt_" == "$2"* ]]; then + COMPREPLY+=("$_opt_") + fi + done +} + +complete -F __wpscan_completion wpscan diff --git a/docs/README.md b/docs/README.md index 33c1b03f..f3d31a14 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,6 +1,6 @@ ![logo](https://github.com/Bash-it/media/raw/master/media/Bash-it.png) -![Build Status](https://github.com/Bash-it/bash-it/workflows/CI/badge.svg?branch=master) +![Build Status](../../../workflows/CI/badge.svg?event=push) ![Docs Status](https://readthedocs.org/projects/bash-it/badge/) ![License](https://img.shields.io/github/license/Bash-it/bash-it) ![shell](https://img.shields.io/badge/Shell-Bash-blue) diff --git a/docs/commands/index.rst b/docs/commands/index.rst index 3eee3b3a..3890a139 100644 --- a/docs/commands/index.rst +++ b/docs/commands/index.rst @@ -13,3 +13,4 @@ You should be familiar with them in order to fully utilize Bash-it. search reload doctor + profile diff --git a/docs/commands/profile.rst b/docs/commands/profile.rst new file mode 100644 index 00000000..67ca9b5b --- /dev/null +++ b/docs/commands/profile.rst @@ -0,0 +1,31 @@ +.. _profile: + +Bash-it Profile +--------------- + +Have you ever wanted to port your *Bash-it* configuration into another machine? + +If you did, then ``bash-it profile`` is for you! + +This command can save and load custom *"profile"* files, that can be later +used to load and recreate your configuration, in any machine you would like |:smile:| + +When porting your configuration into a new machine, you just need to save your current profile, copy the resulting *"profile"* file, and load it in the other machine. + +Example +^^^^^^^ + +.. code-block:: bash + + # Saves your current profile + bash-it profile save my_profile + # Load the default profile, which is the one used in the default installation. + bash-it profile load default + + # Do whatever you want: + # Disable stuff + bash-it disable ... + # Enable stuff + bash-it enable ... + # If you want to get back into your original configuration, you can do it easily + bash-it profile load my_profile diff --git a/docs/development.rst b/docs/development.rst index cd5c59b0..4742e971 100644 --- a/docs/development.rst +++ b/docs/development.rst @@ -147,6 +147,11 @@ Plugin Disable Callbacks Plugins can define a function that will be called when the plugin is being disabled. The callback name should be ``{PLUGIN_NAME}_on_disable``\ , you can see ``gitstatus`` for usage example. +Library Finalization Callback +----------------------------- + +Specifically for Bash-it library code, e.g. in the `lib` subdirectory, a hook is available to run some code at the very end of the main loader script after all other code has been loaded. For example, `lib/theme` uses `_bash_it_library_finalize_hook+=('_bash_it_appearance_scm_init')` to add a function to be called after all plugins have been loaded. + Using the pre-commit hook ------------------------- diff --git a/docs/requirements.txt b/docs/requirements.txt index fdb6cc09..b3015c81 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,3 +1,4 @@ sphinx==3.2.1 sphinx-rtd-theme==0.5.0 sphinxemoji==0.1.8 +docutils==0.17.1 diff --git a/docs/themes-list/index.rst b/docs/themes-list/index.rst index 49c5a623..0275001b 100644 --- a/docs/themes-list/index.rst +++ b/docs/themes-list/index.rst @@ -346,6 +346,19 @@ NWinkler :alt: +---- + +.. _oh_my_posh_image: + +Oh-My-Posh +^^^^^^^^^^ + + +.. image:: https://bash-it.github.io/bash-it/docs/images/oh-my-posh.png + :target: https://bash-it.github.io/bash-it/docs/images/oh-my-posh.png + :alt: + + ---- Pete diff --git a/docs/themes-list/oh-my-posh.rst b/docs/themes-list/oh-my-posh.rst new file mode 100644 index 00000000..974adc0f --- /dev/null +++ b/docs/themes-list/oh-my-posh.rst @@ -0,0 +1,15 @@ +.. _oh-my-posh: + +Oh-My-Posh Theme +================ + +The oh-my-posh ״theme״ is really a plug to a whole other system +of managing your prompt. To use it please start here: +`Oh-My-Posh homepage `_ + +It is beyond the scope of bash-it to install and manage oh-my-posh, +this theme is here just to make sure your OMP setup doesn't clash +with other bash-it themes. Once installed, OMP will load a default +OMP theme (jandedobbeleer), which you can then customize or override. + +Note: Nerd Fonts are highly recommended, as most of the themes are graphic candies. diff --git a/docs/themes.rst b/docs/themes.rst index 5b796389..8cfbeb23 100644 --- a/docs/themes.rst +++ b/docs/themes.rst @@ -22,7 +22,7 @@ Examples: # Disable theming export BASH_IT_THEME="" -You can easily preview the themes in your own shell using ``BASH_PREVIEW=true bash-it reload``. +You can easily preview the themes in your own shell using ``bash-it preview``. If you've created your own custom prompts, we'd love it if you shared them with everyone else! Just submit a Pull Request. You can see theme screenshots on `wiki/Themes `_. diff --git a/hooks/dot-bash.sh b/hooks/dot-bash.sh index 7a9dc8bc..253cb595 100755 --- a/hooks/dot-bash.sh +++ b/hooks/dot-bash.sh @@ -18,4 +18,4 @@ for file in "$@"; do fi done -exit $exit_code +exit "${exit_code:-0}" diff --git a/hooks/dot-sh.sh b/hooks/dot-sh.sh index 3fa63216..e1086bb6 100755 --- a/hooks/dot-sh.sh +++ b/hooks/dot-sh.sh @@ -18,4 +18,4 @@ for file in "$@"; do fi done -exit $exit_code +exit "${exit_code:-0}" diff --git a/install.sh b/install.sh index 4940360f..2bb78a3f 100755 --- a/install.sh +++ b/install.sh @@ -83,8 +83,8 @@ function _bash-it_check_for_backup() { fi echo -e "\033[0;33mBackup file already exists. Make sure to backup your .bashrc before running this installation.\033[0m" >&2 - if ! [[ $overwrite_backup ]]; then - while ! [[ $silent ]]; do + if [[ -z "${overwrite_backup}" ]]; then + while [[ -z "${silent}" ]]; do read -e -n 1 -r -p "Would you like to overwrite the existing backup? This will delete your existing backup file ($HOME/$BACKUP_FILE) [y/N] " RESP case $RESP in [yY]) @@ -100,9 +100,9 @@ function _bash-it_check_for_backup() { esac done fi - if ! [[ $overwrite_backup ]]; then + if [[ -z "${overwrite_backup}" ]]; then echo -e "\033[91mInstallation aborted. Please come back soon!\033[m" - if [[ $silent ]]; then + if [[ -n "${silent}" ]]; then echo -e "\033[91mUse \"-f\" flag to force overwrite of backup.\033[m" fi exit 1 @@ -114,8 +114,8 @@ function _bash-it_check_for_backup() { function _bash-it_modify_config_files() { _bash-it_check_for_backup - if ! [[ $silent ]]; then - while ! [[ $append_to_config ]]; do + if [[ -z "${silent}" ]]; then + while [[ -z "${append_to_config}" ]]; do read -e -n 1 -r -p "Would you like to keep your $CONFIG_FILE and append bash-it templates at the end? [y/N] " choice case $choice in [yY]) @@ -131,7 +131,7 @@ function _bash-it_modify_config_files() { esac done fi - if [[ $append_to_config ]]; then + if [[ -n "${append_to_config}" ]]; then # backup/append _bash-it_backup_append else @@ -174,17 +174,17 @@ done shift $((OPTIND - 1)) -if [[ $silent ]] && [[ $interactive ]]; then +if [[ -n "${silent}" && -n "${interactive}" ]]; then echo -e "\033[91mOptions --silent and --interactive are mutually exclusive. Please choose one or the other.\033[m" exit 1 fi -if [[ $no_modify_config ]] && [[ $append_to_config ]]; then +if [[ -n "${no_modify_config}" && -n "${append_to_config}" ]]; then echo -e "\033[91mOptions --no-modify-config and --append-to-config are mutually exclusive. Please choose one or the other.\033[m" exit 1 fi -BASH_IT="$(cd "$(dirname "$0")" && pwd)" +BASH_IT="$(cd "${BASH_SOURCE%/*}" && pwd)" case $OSTYPE in darwin*) @@ -197,7 +197,7 @@ esac BACKUP_FILE=$CONFIG_FILE.bak echo "Installing bash-it" -if ! [[ $no_modify_config ]]; then +if [[ -z "${no_modify_config}" ]]; then _bash-it_modify_config_files fi @@ -208,29 +208,26 @@ export BASH_IT_AUTOMATIC_RELOAD_AFTER_CONFIG_CHANGE='' source "${BASH_IT}"/vendor/github.com/erichs/composure/composure.sh # shellcheck source=./lib/utilities.bash source "$BASH_IT/lib/utilities.bash" +# shellcheck source=./lib/log.bash +source "${BASH_IT}/lib/log.bash" cite _about _param _example _group _author _version # shellcheck source=./lib/helpers.bash source "$BASH_IT/lib/helpers.bash" -if [[ $interactive ]] && ! [[ $silent ]]; then +if [[ -n $interactive && -z "${silent}" ]]; then for type in "aliases" "plugins" "completion"; do - echo -e "\033[0;32mEnabling $type\033[0m" - _bash-it_load_some $type + echo -e "\033[0;32mEnabling ${type}\033[0m" + _bash-it_load_some "$type" done else echo "" - echo -e "\033[0;32mEnabling reasonable defaults\033[0m" - _enable-completion bash-it - _enable-completion system - _enable-plugin base - _enable-plugin alias-completion - _enable-alias general + _bash-it-profile-load "default" fi echo "" echo -e "\033[0;32mInstallation finished successfully! Enjoy bash-it!\033[0m" # shellcheck disable=SC2086 -echo -e "\033[0;32mTo start using it, open a new tab or 'source "$HOME/$CONFIG_FILE"'.\033[0m" +echo -e "\033[0;32mTo start using it, open a new tab or 'source "~/$CONFIG_FILE"'.\033[0m" echo "" echo "To show the available aliases/completions/plugins, type one of the following:" echo " bash-it show aliases" diff --git a/lib/appearance.bash b/lib/appearance.bash index 6d0ef2ff..e77a1a80 100644 --- a/lib/appearance.bash +++ b/lib/appearance.bash @@ -1,19 +1,18 @@ -#!/usr/bin/env bash +# shellcheck shell=bash -# colored ls -export LSCOLORS='Gxfxcxdxdxegedabagacad' +: "${CLICOLOR:=$(tput colors)}" +export CLICOLOR -if [[ -z "$CUSTOM_THEME_DIR" ]]; then - CUSTOM_THEME_DIR="${BASH_IT_CUSTOM:=${BASH_IT}/custom}/themes" -fi +: "${CUSTOM_THEME_DIR:="${BASH_IT_CUSTOM:=${BASH_IT}/custom}/themes"}" # Load the theme -if [[ $BASH_IT_THEME ]]; then - if [[ -f $BASH_IT_THEME ]]; then - source $BASH_IT_THEME - elif [[ -f "$CUSTOM_THEME_DIR/$BASH_IT_THEME/$BASH_IT_THEME.theme.bash" ]]; then - source "$CUSTOM_THEME_DIR/$BASH_IT_THEME/$BASH_IT_THEME.theme.bash" - else - source "$BASH_IT/themes/$BASH_IT_THEME/$BASH_IT_THEME.theme.bash" - fi +# shellcheck disable=SC1090 +if [[ -n "${BASH_IT_THEME:-}" ]]; then + if [[ -f "${BASH_IT_THEME}" ]]; then + source "${BASH_IT_THEME}" + elif [[ -f "$CUSTOM_THEME_DIR/$BASH_IT_THEME/$BASH_IT_THEME.theme.bash" ]]; then + source "$CUSTOM_THEME_DIR/$BASH_IT_THEME/$BASH_IT_THEME.theme.bash" + else + source "$BASH_IT/themes/$BASH_IT_THEME/$BASH_IT_THEME.theme.bash" + fi fi diff --git a/lib/colors.bash b/lib/colors.bash new file mode 100644 index 00000000..e96bba92 --- /dev/null +++ b/lib/colors.bash @@ -0,0 +1,91 @@ +# shellcheck shell=bash +# shellcheck disable=SC2034 +# +# A set of pre-defined color escape codes for use in prompts and with `echo`. + +black="\[\e[0;30m\]" +red="\[\e[0;31m\]" +green="\[\e[0;32m\]" +yellow="\[\e[0;33m\]" +blue="\[\e[0;34m\]" +purple="\[\e[0;35m\]" +cyan="\[\e[0;36m\]" +white="\[\e[0;37m\]" +orange="\[\e[0;91m\]" + +bold_black="\[\e[30;1m\]" +bold_red="\[\e[31;1m\]" +bold_green="\[\e[32;1m\]" +bold_yellow="\[\e[33;1m\]" +bold_blue="\[\e[34;1m\]" +bold_purple="\[\e[35;1m\]" +bold_cyan="\[\e[36;1m\]" +bold_white="\[\e[37;1m\]" +bold_orange="\[\e[91;1m\]" + +underline_black="\[\e[30;4m\]" +underline_red="\[\e[31;4m\]" +underline_green="\[\e[32;4m\]" +underline_yellow="\[\e[33;4m\]" +underline_blue="\[\e[34;4m\]" +underline_purple="\[\e[35;4m\]" +underline_cyan="\[\e[36;4m\]" +underline_white="\[\e[37;4m\]" +underline_orange="\[\e[91;4m\]" + +background_black="\[\e[40m\]" +background_red="\[\e[41m\]" +background_green="\[\e[42m\]" +background_yellow="\[\e[43m\]" +background_blue="\[\e[44m\]" +background_purple="\[\e[45m\]" +background_cyan="\[\e[46m\]" +background_white="\[\e[47;1m\]" +background_orange="\[\e[101m\]" + +normal="\[\e[0m\]" +reset_color="\[\e[39m\]" + +# These colors are meant to be used with `echo -e` +echo_black="\033[0;30m" +echo_red="\033[0;31m" +echo_green="\033[0;32m" +echo_yellow="\033[0;33m" +echo_blue="\033[0;34m" +echo_purple="\033[0;35m" +echo_cyan="\033[0;36m" +echo_white="\033[0;37;1m" +echo_orange="\033[0;91m" + +echo_bold_black="\033[30;1m" +echo_bold_red="\033[31;1m" +echo_bold_green="\033[32;1m" +echo_bold_yellow="\033[33;1m" +echo_bold_blue="\033[34;1m" +echo_bold_purple="\033[35;1m" +echo_bold_cyan="\033[36;1m" +echo_bold_white="\033[37;1m" +echo_bold_orange="\033[91;1m" + +echo_underline_black="\033[30;4m" +echo_underline_red="\033[31;4m" +echo_underline_green="\033[32;4m" +echo_underline_yellow="\033[33;4m" +echo_underline_blue="\033[34;4m" +echo_underline_purple="\033[35;4m" +echo_underline_cyan="\033[36;4m" +echo_underline_white="\033[37;4m" +echo_underline_orange="\033[91;4m" + +echo_background_black="\033[40m" +echo_background_red="\033[41m" +echo_background_green="\033[42m" +echo_background_yellow="\033[43m" +echo_background_blue="\033[44m" +echo_background_purple="\033[45m" +echo_background_cyan="\033[46m" +echo_background_white="\033[47;1m" +echo_background_orange="\033[101m" + +echo_normal="\033[0m" +echo_reset_color="\033[39m" diff --git a/lib/command_duration.bash b/lib/command_duration.bash new file mode 100644 index 00000000..cd1d6636 --- /dev/null +++ b/lib/command_duration.bash @@ -0,0 +1,61 @@ +# shellcheck shell=bash +# +# Functions for measuring and reporting how long a command takes to run. + +: "${COMMAND_DURATION_START_SECONDS:=${EPOCHREALTIME:-$SECONDS}}" +: "${COMMAND_DURATION_ICON:=🕘}" +: "${COMMAND_DURATION_MIN_SECONDS:=1}" + +function _command_duration_pre_exec() { + COMMAND_DURATION_START_SECONDS="${EPOCHREALTIME:-$SECONDS}" +} + +function _dynamic_clock_icon { + local clock_hand + # clock hand value is between 90 and 9b in hexadecimal. + # so between 144 and 155 in base 10. + printf -v clock_hand '%x' $(((${1:-${SECONDS}} % 12) + 144)) + printf -v 'COMMAND_DURATION_ICON' '%b' "\xf0\x9f\x95\x$clock_hand" +} + +function _command_duration() { + [[ -n "${BASH_IT_COMMAND_DURATION:-}" ]] || return + + local command_duration=0 command_start="${COMMAND_DURATION_START_SECONDS:-0}" + local -i minutes=0 seconds=0 deciseconds=0 + local -i command_start_seconds="${command_start%.*}" + local -i command_start_deciseconds=$((10#${command_start##*.})) + command_start_deciseconds="${command_start_deciseconds:0:1}" + local current_time="${EPOCHREALTIME:-$SECONDS}" + local -i current_time_seconds="${current_time%.*}" + local -i current_time_deciseconds="$((10#${current_time##*.}))" + current_time_deciseconds="${current_time_deciseconds:0:1}" + + if [[ "${command_start_seconds:-0}" -gt 0 ]]; then + # seconds + command_duration="$((current_time_seconds - command_start_seconds))" + + 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)))" + fi + else + command_duration=0 + fi + + if ((command_duration > 0)); then + minutes=$((command_duration / 60)) + seconds=$((command_duration % 60)) + fi + + _dynamic_clock_icon "${command_duration}" + if ((minutes > 0)); then + printf "%s %s%dm %ds" "${COMMAND_DURATION_ICON:-}" "${COMMAND_DURATION_COLOR:-}" "$minutes" "$seconds" + elif ((seconds >= COMMAND_DURATION_MIN_SECONDS)); then + printf "%s %s%d.%01ds" "${COMMAND_DURATION_ICON:-}" "${COMMAND_DURATION_COLOR:-}" "$seconds" "$deciseconds" + fi +} + +_bash_it_library_finalize_hook+=("safe_append_preexec '_command_duration_pre_exec'") diff --git a/lib/helpers.bash b/lib/helpers.bash old mode 100755 new mode 100644 index a528c14c..c0ce2eb3 --- a/lib/helpers.bash +++ b/lib/helpers.bash @@ -1,71 +1,90 @@ -#!/usr/bin/env bash +# shellcheck shell=bash +# shellcheck disable=SC2016 +# +# A collection of reusable functions. -BASH_IT_LOAD_PRIORITY_DEFAULT_ALIAS=${BASH_IT_LOAD_PRIORITY_DEFAULT_ALIAS:-150} -BASH_IT_LOAD_PRIORITY_DEFAULT_PLUGIN=${BASH_IT_LOAD_PRIORITY_DEFAULT_PLUGIN:-250} -BASH_IT_LOAD_PRIORITY_DEFAULT_COMPLETION=${BASH_IT_LOAD_PRIORITY_DEFAULT_COMPLETION:-350} +: "${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 ''` +# Handle the different ways of running `sed` without generating a backup file based on provenance: +# - GNU sed (Linux) uses `-i''` +# - BSD sed (FreeBSD/macOS/Solaris/PlayStation) 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) -case "$OSTYPE" in - 'darwin'*) BASH_IT_SED_I_PARAMETERS=(-i "") -esac +# shellcheck disable=SC2034 # expected for this case +if sed --version > /dev/null 2>&1; then + # GNU sed accepts "long" options + BASH_IT_SED_I_PARAMETERS=('-i') +else + # BSD sed errors on invalid option `-` + BASH_IT_SED_I_PARAMETERS=('-i' '') +fi -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!}" - type "$1" &> /dev/null || (_log_warning "$msg" && return 1) ; +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!}" - type -P "$1" &> /dev/null || (_log_warning "$msg" && return 1) ; +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!}" - complete -p "$1" &> /dev/null && _log_warning "$msg" ; +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 +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. - BASH_IT_HOMEBREW_PREFIX= # clear variable, if set to anything. + 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}" + echo "source '${BASH_IT?}/scripts/reloader.bash' '${1?}' '${2?}'" } # Alias for reloading aliases @@ -80,760 +99,941 @@ alias reload_completion="$(_make_reload_alias completion completion)" # shellcheck disable=SC2139 alias reload_plugins="$(_make_reload_alias plugin plugins)" -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 doctor errors|warnings|all' - typeset verb=${1:-} - shift - typeset component=${1:-} - shift - typeset func +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;; - 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 + 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 + # 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 [ x"$verb" == x"enable" ] || [ x"$verb" == x"disable" ]; then - # Automatically run a migration if required - _bash-it-migrate + if [[ "$verb" == "enable" || "$verb" == "disable" ]]; then + # Automatically run a migration if required + _bash-it-migrate - for arg in "$@" - do - $func $arg - done + for arg in "$@"; do + "$func" "$arg" + done - if [ -n "${BASH_IT_AUTOMATIC_RELOAD_AFTER_CONFIG_CHANGE:-}" ]; then - _bash-it-reload - fi - else - $func "$@" - fi + if [[ -n "${BASH_IT_AUTOMATIC_RELOAD_AFTER_CONFIG_CHANGE:-}" ]]; then + _bash-it-reload + fi + else + "$func" "$@" + fi } -_is_function () -{ - _about 'sets $? to true if parameter is the name of a function' - _param '1: name of alleged function' - _group 'lib' - [ -n "$(LANG=C type -t $1 2>/dev/null | grep 'function')" ] +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 } -_bash-it-aliases () -{ - _about 'summarizes available bash_it aliases' - _group 'lib' +function _bash-it-aliases() { + _about 'summarizes available bash_it aliases' + _group 'lib' - _bash-it-describe "aliases" "an" "alias" "Alias" + _bash-it-describe "aliases" "an" "alias" "Alias" } -_bash-it-completions () -{ - _about 'summarizes available bash_it completions' - _group 'lib' +function _bash-it-completions() { + _about 'summarizes available bash_it completions' + _group 'lib' - _bash-it-describe "completion" "a" "completion" "Completion" + _bash-it-describe "completion" "a" "completion" "Completion" } -_bash-it-plugins () -{ - _about 'summarizes available bash_it plugins' - _group 'lib' +function _bash-it-plugins() { + _about 'summarizes available bash_it plugins' + _group 'lib' - _bash-it-describe "plugins" "a" "plugin" "Plugin" + _bash-it-describe "plugins" "a" "plugin" "Plugin" } -_bash-it-update-dev() { - _about 'updates Bash-it to the latest master' - _group 'lib' +function _bash-it-update-dev() { + _about 'updates Bash-it to the latest master' + _group 'lib' - _bash-it-update- dev "$@" + _bash-it-update- dev "$@" } -_bash-it-update-stable() { - _about 'updates Bash-it to the latest tag' - _group 'lib' +function _bash-it-update-stable() { + _about 'updates Bash-it to the latest tag' + _group 'lib' - _bash-it-update- stable "$@" + _bash-it-update- stable "$@" } -_bash-it_update_migrate_and_restart() { +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' - git checkout "$1" &> /dev/null - if [[ $? -eq 0 ]]; 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! - popd &> /dev/null - _bash-it-restart - else - echo "Error updating Bash-it, please, check if your Bash-it installation folder (${BASH_IT}) is clean." - fi + _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 } -_bash-it-update-() { - _about 'updates Bash-it' - _param '1: What kind of update to do (stable|dev)' - _group 'lib' +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 + local silent word DIFF version TARGET revision status revert log_color RESP + for word in "$@"; do + if [[ "${word}" == "--silent" || "${word}" == "-s" ]]; then + silent=true + fi + done - pushd "${BASH_IT}" &> /dev/null || return + pushd "${BASH_IT?}" > /dev/null || return - DIFF=$(git diff --name-status) - [ -n "$DIFF" ] && echo -e "Local changes detected in bash-it directory. Clean '$BASH_IT' directory to proceed.\n$DIFF" && return 1 + 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 + if [[ -z "$BASH_IT_REMOTE" ]]; then + BASH_IT_REMOTE="origin" + fi - git fetch $BASH_IT_REMOTE --tags &> /dev/null + 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 "$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 - fi - else - version="dev" - TARGET=${BASH_IT_REMOTE}/${BASH_IT_DEVELOPMENT_BRANCH} - fi + 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 + 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 [[ -z "${status}" && "${version}" == "stable" ]]; then + revision="${TARGET}..HEAD" + status="$(git rev-list "${revision}" 2> /dev/null)" + revert=true + fi - if [[ -n "${status}" ]]; then - if [[ $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 + 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 'NF' | wc -l) - 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 "" + git log --format="${log_color}%h: %s (%an)" "${revision}" + echo "" - if [[ $silent ]]; then - echo "Updating to ${TARGET}($(git log -1 --format=%h "${TARGET}"))..." - _bash-it_update_migrate_and_restart $TARGET $version - else - read -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 "\033[91mPlease choose y or n.\033[m" - ;; - 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 + 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 } -_bash-it-migrate() { - _about 'migrates Bash-it configuration from a previous format to the current one' - _group 'lib' +function _bash-it-migrate() { + _about 'migrates Bash-it configuration from a previous format to the current one' + _group 'lib' - declare migrated_something - migrated_something=false + 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 f in `sort <(compgen -G "${BASH_IT}/$file_type/enabled/*.bash")` - do - typeset ff=$(basename $f) + 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 - typeset single_type=$(echo $ff | sed -e 's/.*\.\(.*\)\.bash/\1/g' | sed 's/aliases/alias/g') - # Cut off the optional "250---" prefix and the suffix - typeset component_name=$(echo $ff | sed -e 's/[0-9]*[-]*\(.*\)\..*\.bash/\1/g') + # 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 + migrated_something=true - echo "Migrating $single_type $component_name." + single_type="${component_type/aliases/aliass}" + echo "Migrating ${single_type%s} $component_name." - disable_func="_disable-$single_type" - enable_func="_enable-$single_type" + disable_func="_disable-${single_type%s}" + enable_func="_enable-${single_type%s}" - $disable_func "$component_name" - $enable_func "$component_name" - done - done + "$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 [[ -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 + if [[ "$migrated_something" == "true" ]]; then + echo "" + echo "If any migration errors were reported, please try the following: reload && bash-it migrate" + fi } -_bash-it-version() { - _about 'shows current Bash-it version' - _group 'lib' +function _bash-it-version() { + _about 'shows current Bash-it version' + _group 'lib' - cd "${BASH_IT}" || return + local BASH_IT_GIT_REMOTE BASH_IT_GIT_URL current_tag BASH_IT_GIT_VERSION_INFO TARGET - if [ -z "${BASH_IT_REMOTE:-}" ]; then - BASH_IT_REMOTE="origin" - fi + pushd "${BASH_IT?}" > /dev/null || return - 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 + if [[ -z "${BASH_IT_REMOTE:-}" ]]; then + BASH_IT_REMOTE="origin" + fi - current_tag=$(git describe --exact-match --tags 2> /dev/null) + 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 - 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 + current_tag="$(git describe --exact-match --tags 2> /dev/null)" - echo "Compare to latest: $BASH_IT_GIT_URL/compare/$TARGET...master" + 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 - cd - &> /dev/null || return + echo "Compare to latest: $BASH_IT_GIT_URL/compare/$TARGET...master" + + popd > /dev/null || return } -_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' +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' - BASH_IT_LOG_LEVEL=$1 - _bash-it-reload - unset BASH_IT_LOG_LEVEL + # shellcheck disable=SC2034 # expected for this case + local BASH_IT_LOG_LEVEL="${1?}" + _bash-it-reload } -_bash-it-doctor-all() { - _about 'reloads a profile file with error, warning and debug logs' - _group 'lib' +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 + _bash-it-doctor "${BASH_IT_LOG_LEVEL_ALL?}" } -_bash-it-doctor-warnings() { - _about 'reloads a profile file with error and warning logs' - _group 'lib' +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 + _bash-it-doctor "${BASH_IT_LOG_LEVEL_WARNING?}" } -_bash-it-doctor-errors() { - _about 'reloads a profile file with error logs' - _group 'lib' +function _bash-it-doctor-errors() { + _about 'reloads a profile file with error logs' + _group 'lib' - _bash-it-doctor $BASH_IT_LOG_LEVEL_ERROR + _bash-it-doctor "${BASH_IT_LOG_LEVEL_ERROR?}" } -_bash-it-doctor-() { - _about 'default bash-it doctor behavior, behaves like bash-it doctor all' - _group 'lib' +function _bash-it-doctor-() { + _about 'default bash-it doctor behavior, behaves like bash-it doctor all' + _group 'lib' - _bash-it-doctor-all + _bash-it-doctor-all } -_bash-it-restart() { - _about 'restarts the shell in order to fully reload it' - _group 'lib' +function _bash-it-profile-save() { + _about 'saves the current configuration to the "profile" directory' + _group 'lib' - saved_pwd=$(pwd) + 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 - case $OSTYPE in - darwin*) - init_file=.bash_profile - ;; - *) - init_file=.bashrc - ;; - esac - exec "${0/-/}" --rcfile <(echo "source \"$HOME/$init_file\"; cd \"$saved_pwd\"") + 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-reload() { - _about 'reloads a profile file' - _group 'lib' +_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"' - pushd "${BASH_IT}" &> /dev/null || return + local -i num=0 + local line enable_func subdirectory component to_enable bad + while read -r -a line; do + ((++num)) + # Ignore comments and empty lines + [[ -z "${line[*]}" || "${line[*]}" =~ ^#.* ]] && continue + enable_func="_enable-${line[0]}" + subdirectory=${line[0]} + component=${line[1]} - case $OSTYPE in - darwin*) - source ~/.bash_profile - ;; - *) - source ~/.bashrc - ;; - esac + 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?}" + 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?}" - popd &> /dev/null || return + # Make sure to propagate the error + [[ -z ${bad:-} ]] } -_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"' +_bash-it-profile-list() { + about 'lists all profiles from the "profiles" directory' + _group 'lib' + local profile - subdirectory="$1" - preposition="$2" - file_type="$3" - column_header="$4" - - typeset f - typeset enabled - printf "%-20s%-10s%s\n" "$column_header" 'Enabled?' 'Description' - for f in "${BASH_IT}/$subdirectory/available/"*.bash - do - # Check for both the old format without the load priority, and the extended format with the priority - declare enabled_files enabled_file - enabled_file=$(basename $f) - enabled_files=$(sort <(compgen -G "${BASH_IT}/enabled/*$BASH_IT_LOAD_PRIORITY_SEPARATOR${enabled_file}") <(compgen -G "${BASH_IT}/$subdirectory/enabled/${enabled_file}") <(compgen -G "${BASH_IT}/$subdirectory/enabled/*$BASH_IT_LOAD_PRIORITY_SEPARATOR${enabled_file}") | wc -l) - - if [ $enabled_files -gt 0 ]; then - enabled='x' - else - enabled=' ' - fi - printf "%-20s%-10s%s\n" "$(basename $f | sed -e 's/\(.*\)\..*\.bash/\1/g')" " [$enabled]" "$(cat $f | metafor about-$file_type)" - 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" + echo "Available profiles:" + for profile in "${BASH_IT}/profiles"/*.bash_it; do + profile="${profile##*/}" + echo "${profile/.bash_it/}" + done } -_on-disable-callback() -{ - _about 'Calls the disabled plugin destructor, if present' - _param '1: plugin name' - _example '$ _on-disable-callback gitstatus' - _group 'lib' +_bash-it-profile-rm() { + about 'Removes a profile from the "profiles" directory' + _group 'lib' - callback=$1_on_disable - _command_exists $callback && $callback + 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!" } -_disable-plugin () -{ - _about 'disables bash_it plugin' - _param '1: plugin name' - _example '$ disable-plugin rvm' - _group 'lib' +_bash-it-profile-load() { + _about 'loads a configuration from the "profiles" directory' + _group 'lib' - _disable-thing "plugins" "plugin" $1 - _on-disable-callback $1 + 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 } -_disable-alias () -{ - _about 'disables bash_it alias' - _param '1: alias name' - _example '$ disable-alias git' - _group 'lib' +function _bash-it-restart() { + _about 'restarts the shell in order to fully reload it' + _group 'lib' - _disable-thing "aliases" "alias" $1 + exec "${0#-}" --rcfile "${BASH_IT_BASHRC:-${HOME?}/.bashrc}" } -_disable-completion () -{ - _about 'disables bash_it completion' - _param '1: completion name' - _example '$ disable-completion git' - _group 'lib' +function _bash-it-reload() { + _about 'reloads the shell initialization file' + _group 'lib' - _disable-thing "completion" "completion" $1 + # shellcheck disable=SC1090 + source "${BASH_IT_BASHRC:-${HOME?}/.bashrc}" } -_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"' +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" - file_type="$2" - file_entity="$3" + local subdirectory preposition file_type column_header f enabled enabled_file + subdirectory="$1" + preposition="$2" + file_type="$3" + column_header="$4" - if [ -z "$file_entity" ]; then - reference "disable-$file_type" - return - fi - - typeset f suffix - suffix=$(echo "$subdirectory" | sed -e 's/plugins/plugin/g') - - if [ "$file_entity" = "all" ]; then - # Disable everything that's using the old structure - for f in `compgen -G "${BASH_IT}/$subdirectory/enabled/*.${suffix}.bash"` - do - rm "$f" - done - - # Disable everything in the global "enabled" directory - for f in `compgen -G "${BASH_IT}/enabled/*.${suffix}.bash"` - do - rm "$f" - done - else - typeset plugin_global=$(command ls $ "${BASH_IT}/enabled/"[0-9]*$BASH_IT_LOAD_PRIORITY_SEPARATOR$file_entity.$suffix.bash 2>/dev/null | head -1) - if [ -z "$plugin_global" ]; then - # Use a glob to search for both possible patterns - # 250---node.plugin.bash - # node.plugin.bash - # Either one will be matched by this glob - typeset plugin=$(command ls $ "${BASH_IT}/$subdirectory/enabled/"{[0-9]*$BASH_IT_LOAD_PRIORITY_SEPARATOR$file_entity.$suffix.bash,$file_entity.$suffix.bash} 2>/dev/null | head -1) - if [ -z "$plugin" ]; then - printf '%s\n' "sorry, $file_entity does not appear to be an enabled $file_type." - return - fi - rm "${BASH_IT}/$subdirectory/enabled/$(basename $plugin)" - else - rm "${BASH_IT}/enabled/$(basename $plugin_global)" - fi - fi - - _bash-it-clean-component-cache "${file_type}" - - printf '%s\n' "$file_entity disabled." + 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" } -_enable-plugin () -{ - _about 'enables bash_it plugin' - _param '1: plugin name' - _example '$ enable-plugin rvm' - _group 'lib' +function _on-disable-callback() { + _about 'Calls the disabled plugin destructor, if present' + _param '1: plugin name' + _example '$ _on-disable-callback gitstatus' + _group 'lib' - _enable-thing "plugins" "plugin" $1 $BASH_IT_LOAD_PRIORITY_DEFAULT_PLUGIN + local callback="${1}_on_disable" + if _command_exists "$callback"; then + "$callback" + fi } -_enable-alias () -{ - _about 'enables bash_it alias' - _param '1: alias name' - _example '$ enable-alias git' - _group 'lib' +function _disable-all() { + _about 'disables all bash_it components' + _example '$ _disable-all' + _group 'lib' - _enable-thing "aliases" "alias" $1 $BASH_IT_LOAD_PRIORITY_DEFAULT_ALIAS + _disable-plugin "all" + _disable-alias "all" + _disable-completion "all" } -_enable-completion () -{ - _about 'enables bash_it completion' - _param '1: completion name' - _example '$ enable-completion git' - _group 'lib' +function _disable-plugin() { + _about 'disables bash_it plugin' + _param '1: plugin name' + _example '$ disable-plugin rvm' + _group 'lib' - _enable-thing "completion" "completion" $1 $BASH_IT_LOAD_PRIORITY_DEFAULT_COMPLETION + _disable-thing "plugins" "plugin" "${1?}" + _on-disable-callback "${1?}" } -_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"' +function _disable-alias() { + _about 'disables bash_it alias' + _param '1: alias name' + _example '$ disable-alias git' + _group 'lib' - subdirectory="$1" - file_type="$2" - file_entity="$3" - load_priority="$4" - - if [ -z "$file_entity" ]; then - reference "enable-$file_type" - return - fi - - if [ "$file_entity" = "all" ]; then - typeset f $file_type - for f in "${BASH_IT}/$subdirectory/available/"*.bash - do - to_enable=$(basename $f .$file_type.bash) - if [ "$file_type" = "alias" ]; then - to_enable=$(basename $f ".aliases.bash") - fi - _enable-thing $subdirectory $file_type $to_enable $load_priority - done - else - typeset to_enable=$(command ls "${BASH_IT}/$subdirectory/available/"$file_entity.*bash 2>/dev/null | head -1) - if [ -z "$to_enable" ]; then - printf '%s\n' "sorry, $file_entity does not appear to be an available $file_type." - return - fi - - to_enable=$(basename $to_enable) - # Check for existence of the file using a wildcard, since we don't know which priority might have been used when enabling it. - typeset enabled_plugin=$(command ls "${BASH_IT}/$subdirectory/enabled/"{[0-9][0-9][0-9]$BASH_IT_LOAD_PRIORITY_SEPARATOR$to_enable,$to_enable} 2>/dev/null | head -1) - if [ ! -z "$enabled_plugin" ] ; then - printf '%s\n' "$file_entity is already enabled." - return - fi - - typeset enabled_plugin_global=$(command compgen -G "${BASH_IT}/enabled/[0-9][0-9][0-9]$BASH_IT_LOAD_PRIORITY_SEPARATOR$to_enable" 2>/dev/null | head -1) - if [ ! -z "$enabled_plugin_global" ] ; then - printf '%s\n' "$file_entity is already enabled." - return - fi - - mkdir -p "${BASH_IT}/enabled" - - # Load the priority from the file if it present there - declare local_file_priority use_load_priority - local_file_priority=$(grep -E "^# 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." + _disable-thing "aliases" "alias" "${1?}" } -_help-completions() -{ - _about 'summarize all completions available in bash-it' - _group 'lib' +function _disable-completion() { + _about 'disables bash_it completion' + _param '1: completion name' + _example '$ disable-completion git' + _group 'lib' - _bash-it-completions + _disable-thing "completion" "completion" "${1?}" } -_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' +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"' - if [ -n "$1" ]; then - case $1 in - custom) - alias_path='custom.aliases.bash' - ;; - *) - alias_path="available/$1.aliases.bash" - ;; - esac - cat "${BASH_IT}/aliases/$alias_path" | metafor alias | sed "s/$/'/" - else - typeset f + local subdirectory="${1?}" + local file_type="${2?}" + local file_entity="${3:-}" - for f in `sort <(compgen -G "${BASH_IT}/aliases/enabled/*") <(compgen -G "${BASH_IT}/enabled/*.aliases.bash")` - do - _help-list-aliases $f - done + if [[ -z "$file_entity" ]]; then + reference "disable-$file_type" + return + fi - if [ -e "${BASH_IT}/aliases/custom.aliases.bash" ]; then - _help-list-aliases "${BASH_IT}/aliases/custom.aliases.bash" - fi - 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 -f "${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-component-cache-clean "${file_type}" + + if [[ "$file_entity" == "all" ]]; then + _bash-it-component-pluralize "$file_type" file_type + printf '%s\n' "$file_entity ${file_type} disabled." + else + printf '%s\n' "$file_entity disabled." + fi } -_help-list-aliases () -{ - typeset file=$(basename $1 | sed -e 's/[0-9]*[-]*\(.*\)\.aliases\.bash/\1/g') - printf '\n\n%s:\n' "${file}" - # metafor() strips trailing quotes, restore them with sed.. - cat $1 | metafor alias | sed "s/$/'/" +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" } -_help-plugins() -{ - _about 'summarize all functions defined by enabled bash-it plugins' - _group 'lib' - - # display a brief progress message... - printf '%s' 'please wait, building help...' - typeset grouplist=$(mktemp -t grouplist.XXXXXX) - typeset func - for func in $(_typeset_functions) - do - typeset group="$(typeset -f $func | metafor group)" - if [ -z "$group" ]; then - group='misc' - fi - typeset about="$(typeset -f $func | metafor about)" - _letterpress "$about" $func >> $grouplist.$group - echo $grouplist.$group >> $grouplist - done - # clear progress message - printf '\r%s\n' ' ' - typeset group - typeset gfile - for gfile in $(cat $grouplist | sort | uniq) - do - printf '%s\n' "${gfile##*.}:" - cat $gfile - printf '\n' - rm $gfile 2> /dev/null - done | less - rm $grouplist 2> /dev/null +function _enable-plugins() { + _about 'alias of _enable-plugin' + _enable-plugin "$@" } -_help-update () { - _about 'help message for update command' - _group 'lib' +function _enable-alias() { + _about 'enables bash_it alias' + _param '1: alias name' + _example '$ enable-alias git' + _group 'lib' - echo "Check for a new version of Bash-it and update it." + _enable-thing "aliases" "alias" "${1?}" "$BASH_IT_LOAD_PRIORITY_ALIAS" } -_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 _enable-aliases() { + _about 'alias of _enable-alias' + _enable-alias "$@" } -all_groups () -{ - about 'displays all unique metadata groups' - group 'lib' +function _enable-completion() { + _about 'enables bash_it completion' + _param '1: completion name' + _example '$ enable-completion git' + _group 'lib' - typeset func - typeset file=$(mktemp -t composure.XXXX) - for func in $(_typeset_functions) - do - typeset -f $func | metafor group >> $file - done - cat $file | sort | uniq - rm $file + _enable-thing "completion" "completion" "${1?}" "$BASH_IT_LOAD_PRIORITY_COMPLETION" } -if ! type pathmunge > /dev/null 2>&1 -then - 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' +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"' - if ! [[ $PATH =~ (^|:)$1($|:) ]] ; then - if [ "$2" = "after" ] ; then - export PATH=$PATH:$1 - else - export PATH=$1:$PATH - fi - fi - } -fi + 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-component-cache-clean "${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 +) diff --git a/lib/history.bash b/lib/history.bash new file mode 100644 index 00000000..7bdbbd5e --- /dev/null +++ b/lib/history.bash @@ -0,0 +1,49 @@ +# shellcheck shell=bash +# +# Functions for working with Bash's command history. + +function _bash-it-history-init() { + safe_append_preexec '_bash-it-history-auto-save' + safe_append_prompt_command '_bash-it-history-auto-load' +} + +function _bash-it-history-auto-save() { + case $HISTCONTROL in + *'noauto'* | *'autoload'*) + : # Do nothing, as configured. + ;; + *'auto'*) + # Append new history from this session to the $HISTFILE + history -a + ;; + *) + # Append *only* if shell option `histappend` has been enabled. + shopt -q histappend && history -a && return + ;; + esac +} + +function _bash-it-history-auto-load() { + case $HISTCONTROL in + *'noauto'*) + : # Do nothing, as configured. + ;; + *'autosave'*) + # Append new history from this session to the $HISTFILE + history -a + ;; + *'autoloadnew'*) + # Read new entries from $HISTFILE + history -n + ;; + *'auto'*) + # Blank in-memory history, then read entire $HISTFILE fresh from disk. + history -a && history -c && history -r + ;; + *) + : # Do nothing, default. + ;; + esac +} + +_bash_it_library_finalize_hook+=('_bash-it-history-init') diff --git a/lib/log.bash b/lib/log.bash old mode 100755 new mode 100644 index 6bc53a12..a8cb8080 --- a/lib/log.bash +++ b/lib/log.bash @@ -1,60 +1,100 @@ -#!/usr/bin/env bash +# shellcheck shell=bash +# +# A collection of logging functions. -export BASH_IT_LOG_LEVEL_ERROR=1 -export BASH_IT_LOG_LEVEL_WARNING=2 -export BASH_IT_LOG_LEVEL_ALL=3 +# Declare log severity levels, matching syslog numbering +: "${BASH_IT_LOG_LEVEL_FATAL:=1}" +: "${BASH_IT_LOG_LEVEL_ERROR:=3}" +: "${BASH_IT_LOG_LEVEL_WARNING:=4}" +: "${BASH_IT_LOG_LEVEL_ALL:=6}" +: "${BASH_IT_LOG_LEVEL_INFO:=6}" +: "${BASH_IT_LOG_LEVEL_TRACE:=7}" +readonly "${!BASH_IT_LOG_LEVEL_@}" -function _has_colors() -{ - # Check that stdout is a terminal - test -t 1 || return 1 +function _bash-it-log-prefix-by-path() { + local component_path="${1?${FUNCNAME[0]}: path specification required}" + local without_extension component_directory + local component_filename component_type component_name - ncolors=$(tput colors) - test -n "$ncolors" && test "$ncolors" -ge 8 || return 1 - return 0 + # get the directory, if any + component_directory="${component_path%/*}" + # drop the directory, if any + component_filename="${component_path##*/}" + # strip the file extension + without_extension="${component_filename%.bash}" + # strip before the last dot + component_type="${without_extension##*.}" + # strip component type, but try not to strip other words + # - aliases, completions, plugins, themes + component_name="${without_extension%.[acpt][hlo][eimu]*[ens]}" + # Finally, strip load priority prefix + component_name="${component_name##[[:digit:]][[:digit:]][[:digit:]]"${BASH_IT_LOAD_PRIORITY_SEPARATOR:----}"}" + + # best-guess for files without a type + if [[ "${component_type:-${component_name}}" == "${component_name}" ]]; then + if [[ "${component_directory}" == *'vendor'* ]]; then + component_type='vendor' + else + component_type="${component_directory##*/}" + fi + fi + + # shellcheck disable=SC2034 + BASH_IT_LOG_PREFIX="${component_type:-lib}: $component_name" } -function _log_general() -{ - about 'Internal function used for logging, uses BASH_IT_LOG_PREFIX as a prefix' - param '1: color of the message' - param '2: log level to print before the prefix' - param '3: message to log' - group 'log' - - message=$2${BASH_IT_LOG_PREFIX:-default: }$3 - _has_colors && echo -e "$1${message}${echo_normal:-}" || echo -e "${message}" +function _has_colors() { + # Check that stdout is a terminal, and that it has at least 8 colors. + [[ -t 1 && "${CLICOLOR:=$(tput colors 2> /dev/null)}" -ge 8 ]] } -function _log_debug() -{ - about 'log a debug message by echoing to the screen. needs BASH_IT_LOG_LEVEL >= BASH_IT_LOG_LEVEL_ALL' - param '1: message to log' - example '$ _log_debug "Loading plugin git..."' - group 'log' +function _bash-it-log-message() { + : _about 'Internal function used for logging, uses BASH_IT_LOG_PREFIX as a prefix' + : _param '1: color of the message' + : _param '2: log level to print before the prefix' + : _param '3: message to log' + : _group 'log' - [[ "${BASH_IT_LOG_LEVEL:-0}" -ge $BASH_IT_LOG_LEVEL_ALL ]] || return 0 - _log_general "${echo_green:-}" "DEBUG: " "$1" + local prefix="${BASH_IT_LOG_PREFIX:-default}" + local color="${1-${echo_cyan:-}}" + local level="${2:-TRACE}" + local message="${level%: }: ${prefix%: }: ${3?}" + if _has_colors; then + printf '%b%s%b\n' "${color}" "${message}" "${echo_normal:-}" + else + printf '%s\n' "${message}" + fi } -function _log_warning() -{ - about 'log a message by echoing to the screen. needs BASH_IT_LOG_LEVEL >= BASH_IT_LOG_LEVEL_WARNING' - param '1: message to log' - example '$ _log_warning "git binary not found, disabling git plugin..."' - group 'log' +function _log_debug() { + : _about 'log a debug message by echoing to the screen. needs BASH_IT_LOG_LEVEL >= BASH_IT_LOG_LEVEL_INFO' + : _param '1: message to log' + : _example '$ _log_debug "Loading plugin git..."' + : _group 'log' - [[ "${BASH_IT_LOG_LEVEL:-0}" -ge $BASH_IT_LOG_LEVEL_WARNING ]] || return 0 - _log_general "${echo_yellow:-}" " WARN: " "$1" + if [[ "${BASH_IT_LOG_LEVEL:-0}" -ge "${BASH_IT_LOG_LEVEL_INFO?}" ]]; then + _bash-it-log-message "${echo_green:-}" "DEBUG: " "$1" + fi } -function _log_error() -{ - about 'log a message by echoing to the screen. needs BASH_IT_LOG_LEVEL >= BASH_IT_LOG_LEVEL_ERROR' - param '1: message to log' - example '$ _log_error "Failed to load git plugin..."' - group 'log' +function _log_warning() { + : _about 'log a message by echoing to the screen. needs BASH_IT_LOG_LEVEL >= BASH_IT_LOG_LEVEL_WARNING' + : _param '1: message to log' + : _example '$ _log_warning "git binary not found, disabling git plugin..."' + : _group 'log' - [[ "${BASH_IT_LOG_LEVEL:-0}" -ge $BASH_IT_LOG_LEVEL_ERROR ]] || return 0 - _log_general "${echo_red:-}" "ERROR: " "$1" + if [[ "${BASH_IT_LOG_LEVEL:-0}" -ge "${BASH_IT_LOG_LEVEL_WARNING?}" ]]; then + _bash-it-log-message "${echo_yellow:-}" " WARN: " "$1" + fi +} + +function _log_error() { + : _about 'log a message by echoing to the screen. needs BASH_IT_LOG_LEVEL >= BASH_IT_LOG_LEVEL_ERROR' + : _param '1: message to log' + : _example '$ _log_error "Failed to load git plugin..."' + : _group 'log' + + if [[ "${BASH_IT_LOG_LEVEL:-0}" -ge "${BASH_IT_LOG_LEVEL_ERROR?}" ]]; then + _bash-it-log-message "${echo_red:-}" "ERROR: " "$1" + fi } diff --git a/lib/preexec.bash b/lib/preexec.bash new file mode 100644 index 00000000..1dbe8899 --- /dev/null +++ b/lib/preexec.bash @@ -0,0 +1,75 @@ +# shellcheck shell=bash +# shellcheck disable=SC2034 +# +# Load the `bash-preexec.sh` library, and define helper functions + +## Prepare, load, fix, and install `bash-preexec.sh` + +# Disable `$PROMPT_COMMAND` modification for now. +__bp_delay_install="delayed" + +# shellcheck source-path=SCRIPTDIR/../vendor/github.com/rcaloras/bash-preexec +source "${BASH_IT?}/vendor/github.com/rcaloras/bash-preexec/bash-preexec.sh" + +# Block damanaging user's `$HISTCONTROL` +function __bp_adjust_histcontrol() { :; } + +# Don't fail on readonly variables +function __bp_require_not_readonly() { :; } + +# For performance, testing, and to avoid unexpected behavior: disable DEBUG traps in subshells. +# See bash-it/bash-it#1040 and rcaloras/bash-preexec#26 +: "${__bp_enable_subshells:=}" # blank + +# Modify `$PROMPT_COMMAND` in finalize hook +_bash_it_library_finalize_hook+=('__bp_install_after_session_init') + +## Helper functions +function __check_precmd_conflict() { + local f + __bp_trim_whitespace f "${1?}" + _bash-it-array-contains-element "${f}" "${precmd_functions[@]}" +} + +function __check_preexec_conflict() { + local f + __bp_trim_whitespace f "${1?}" + _bash-it-array-contains-element "${f}" "${preexec_functions[@]}" +} + +function safe_append_prompt_command() { + local prompt_re prompt_er f + + if [[ "${bash_preexec_imported:-${__bp_imported:-missing}}" == "defined" ]]; then + # We are using bash-preexec + __bp_trim_whitespace f "${1?}" + if ! __check_precmd_conflict "${f}"; then + precmd_functions+=("${f}") + fi + else + # Match on word-boundaries + prompt_re='(^|[^[:alnum:]_])' + prompt_er='([^[:alnum:]_]|$)' + if [[ ${PROMPT_COMMAND} =~ ${prompt_re}"${1}"${prompt_er} ]]; then + return + elif [[ -z ${PROMPT_COMMAND} ]]; then + PROMPT_COMMAND="${1}" + else + PROMPT_COMMAND="${1};${PROMPT_COMMAND}" + fi + fi +} + +function safe_append_preexec() { + local prompt_re f + + if [[ "${bash_preexec_imported:-${__bp_imported:-missing}}" == "defined" ]]; then + # We are using bash-preexec + __bp_trim_whitespace f "${1?}" + if ! __check_preexec_conflict "${f}"; then + preexec_functions+=("${f}") + fi + else + _log_error "${FUNCNAME[0]}: can't append to preexec hook because _bash-preexec.sh_ hasn't been loaded" + fi +} diff --git a/lib/preview.bash b/lib/preview.bash index 418839cd..46c1618a 100644 --- a/lib/preview.bash +++ b/lib/preview.bash @@ -1,19 +1,34 @@ -if [[ "${BASH_PREVIEW:-}" ]]; -then - unset BASH_PREVIEW #Prevent infinite looping - echo " +# shellcheck shell=bash +# +# Displays the prompt from each _Bash It_ theme. - Previewing Bash-it Themes +function _bash-it-preview() { + local BASH_IT_THEME BASH_IT_LOG_LEVEL + local themes IFS=$'\n' cur - " + if [[ $# -gt '0' ]]; then + themes=("$@") + else + themes=("${BASH_IT?}/themes"/*/*.theme.bash) + themes=("${themes[@]##*/}") + themes=("${themes[@]%.theme.bash}") + fi - THEMES="$BASH_IT/themes/*/*.theme.bash" - for theme in $THEMES - do - BASH_IT_THEME=${theme%.theme.bash} - BASH_IT_THEME=${BASH_IT_THEME##*/} - echo " - $BASH_IT_THEME" - echo "" | bash --init-file "${BASH_IT}/bash_it.sh" -i - done + if [[ ${COMP_CWORD:-} -gt '0' ]]; then + cur="${COMP_WORDS[COMP_CWORD]}" + read -d '' -ra COMPREPLY < <(compgen -W "all${IFS}${themes[*]}" -- "${cur}") + return + fi + printf '\n\n\t%s\n\n' "Previewing Bash-it Themes" + + # shellcheck disable=SC2034 + for BASH_IT_THEME in "${themes[@]}"; do + BASH_IT_LOG_LEVEL=0 + bash --init-file "${BASH_IT?}/bash_it.sh" -i <<< '_bash-it-flash-term "${#BASH_IT_THEME}" "${BASH_IT_THEME}"' + done +} + +if [[ -n "${BASH_PREVIEW:-}" ]]; then + _bash-it-preview "${BASH_PREVIEW}" "$@" + unset BASH_PREVIEW #Prevent infinite looping fi diff --git a/lib/search.bash b/lib/search.bash old mode 100755 new mode 100644 index 58f98904..247e6294 --- a/lib/search.bash +++ b/lib/search.bash @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +# shellcheck shell=bash # # Search by Konstantin Gredeskoul «github.com/kigster» #——————————————————————————————————————————————————————————————————————————————— @@ -47,50 +47,52 @@ # completions: git # -_bash-it-search() { - _about 'searches for given terms amongst bash-it plugins, aliases and completions' - _param '1: term1' - _param '2: [ term2 ]...' - _example '$ _bash-it-search @git ruby -rvm rake bundler' +function _bash-it-search() { + _about 'searches for given terms amongst bash-it plugins, aliases and completions' + _param '1: term1' + _param '2: [ term2 ]...' + _example '$ _bash-it-search @git ruby -rvm rake bundler' - [[ -z "$(type _bash-it-array-contains-element 2>/dev/null)" ]] && source "${BASH_IT}/lib/utilities.bash" + local component + local BASH_IT_SEARCH_USE_COLOR="${BASH_IT_SEARCH_USE_COLOR:=true}" + local -a BASH_IT_COMPONENTS=('aliases' 'plugins' 'completions') - local component - export BASH_IT_SEARCH_USE_COLOR=true - export BASH_IT_GREP=${BASH_IT_GREP:-$(which egrep)} - declare -a BASH_IT_COMPONENTS=(aliases plugins completions) + if [[ $# -eq 0 ]]; then + _bash-it-search-help + return 0 + fi - if [[ -z "$*" ]] ; then - _bash-it-search-help - return 0 - fi + local -a args=() + for word in "$@"; do + case "${word}" in + '-h' | '--help') + _bash-it-search-help + return 0 + ;; + '-r' | '--refresh') + _bash-it-component-cache-clean + ;; + '-c' | '--no-color') + BASH_IT_SEARCH_USE_COLOR=false + ;; + *) + args+=("${word}") + ;; + esac + done - local -a args=() - for word in $@; do - if [[ ${word} == "--help" || ${word} == "-h" ]]; then - _bash-it-search-help - return 0 - elif [[ ${word} == "--refresh" || ${word} == "-r" ]]; then - _bash-it-clean-component-cache - elif [[ ${word} == "--no-color" || ${word} == '-c' ]]; then - export BASH_IT_SEARCH_USE_COLOR=false - else - args=(${args[@]} ${word}) - fi - done + if [[ ${#args} -gt 0 ]]; then + for component in "${BASH_IT_COMPONENTS[@]}"; do + _bash-it-search-component "${component}" "${args[@]}" + done + fi - if [[ ${#args} -gt 0 ]]; then - for component in "${BASH_IT_COMPONENTS[@]}" ; do - _bash-it-search-component "${component}" "${args[@]}" - done - fi - - return 0 + return 0 } -_bash-it-search-help() { - printf "${echo_normal} -${echo_underline_yellow}USAGE${echo_normal} +function _bash-it-search-help() { + printf '%b' "${echo_normal-} +${echo_underline_yellow-}USAGE${echo_normal-} bash-it search [-|@]term1 [-|@]term2 ... \\ [ --enable | -e ] \\ @@ -99,9 +101,9 @@ ${echo_underline_yellow}USAGE${echo_normal} [ --refresh | -r ] \\ [ --help | -h ] -${echo_underline_yellow}DESCRIPTION${echo_normal} +${echo_underline_yellow-}DESCRIPTION${echo_normal-} - Use ${echo_bold_green}search${echo_normal} bash-it command to search for a list of terms or term negations + Use ${echo_bold_green-}search${echo_normal-} bash-it command to search for a list of terms or term negations across all components: aliases, completions and plugins. Components that are enabled are shown in green (or with a check box if --no-color option is used). @@ -118,42 +120,42 @@ ${echo_underline_yellow}DESCRIPTION${echo_normal} * To perform an exact match, use character '@' in front of the term, eg. '@git' would only match aliases, plugins and completions named 'git'. -${echo_underline_yellow}FLAGS${echo_normal} - --enable | -e ${echo_purple}Enable all matching componenents.${echo_normal} - --disable | -d ${echo_purple}Disable all matching componenents.${echo_normal} - --help | -h ${echo_purple}Print this help.${echo_normal} - --refresh | -r ${echo_purple}Force a refresh of the search cache.${echo_normal} - --no-color | -c ${echo_purple}Disable color output and use monochrome text.${echo_normal} +${echo_underline_yellow-}FLAGS${echo_normal-} + --enable | -e ${echo_purple-}Enable all matching componenents.${echo_normal-} + --disable | -d ${echo_purple-}Disable all matching componenents.${echo_normal-} + --help | -h ${echo_purple-}Print this help.${echo_normal-} + --refresh | -r ${echo_purple-}Force a refresh of the search cache.${echo_normal-} + --no-color | -c ${echo_purple-}Disable color output and use monochrome text.${echo_normal-} -${echo_underline_yellow}EXAMPLES${echo_normal} +${echo_underline_yellow-}EXAMPLES${echo_normal-} - For example, ${echo_bold_green}bash-it search git${echo_normal} would match any alias, completion + For example, ${echo_bold_green-}bash-it search git${echo_normal-} would match any alias, completion or plugin that has the word 'git' in either the module name or it's description. You should see something like this when you run this command: - ${echo_bold_green}❯ bash-it search git${echo_bold_blue} - ${echo_bold_yellow}aliases: ${echo_bold_green}git ${echo_normal}gitsvn - ${echo_bold_yellow}plugins: ${echo_normal}autojump ${echo_bold_green}git ${echo_normal}git-subrepo jgitflow jump - ${echo_bold_yellow}completions: ${echo_bold_green}git ${echo_normal}git_flow git_flow_avh${echo_normal} + ${echo_bold_green-}❯ bash-it search git${echo_bold_blue-} + ${echo_bold_yellow-}aliases: ${echo_bold_green-}git ${echo_normal-}gitsvn + ${echo_bold_yellow-}plugins: ${echo_normal-}autojump ${echo_bold_green-}git ${echo_normal-}git-subrepo jgitflow jump + ${echo_bold_yellow-}completions: ${echo_bold_green-}git ${echo_normal-}git_flow git_flow_avh${echo_normal-} You can exclude some terms by prefixing a term with a minus, eg: - ${echo_bold_green}❯ bash-it search git -flow -svn${echo_bold_blue} - ${echo_bold_yellow}aliases: ${echo_normal}git - ${echo_bold_yellow}plugins: ${echo_normal}autojump git git-subrepo jump - ${echo_bold_yellow}completions: ${echo_normal}git${echo_normal} + ${echo_bold_green-}❯ bash-it search git -flow -svn${echo_bold_blue-} + ${echo_bold_yellow-}aliases: ${echo_normal-}git + ${echo_bold_yellow-}plugins: ${echo_normal-}autojump git git-subrepo jump + ${echo_bold_yellow-}completions: ${echo_normal-}git${echo_normal-} Finally, if you prefix a term with '@' symbol, that indicates an exact match. Note, that we also pass the '--enable' flag, which would ensure that all matches are automatically enabled. The example is below: - ${echo_bold_green}❯ bash-it search @git --enable${echo_bold_blue} - ${echo_bold_yellow}aliases: ${echo_normal}git - ${echo_bold_yellow}plugins: ${echo_normal}git - ${echo_bold_yellow}completions: ${echo_normal}git${echo_normal} + ${echo_bold_green-}❯ bash-it search @git --enable${echo_bold_blue-} + ${echo_bold_yellow-}aliases: ${echo_normal-}git + ${echo_bold_yellow-}plugins: ${echo_normal-}git + ${echo_bold_yellow-}completions: ${echo_normal-}git${echo_normal-} -${echo_underline_yellow}SUMMARY${echo_normal} +${echo_underline_yellow-}SUMMARY${echo_normal-} Take advantage of the search functionality to discover what Bash-It can do for you. Try searching for partial term matches, mix and match with the @@ -165,197 +167,207 @@ ${echo_underline_yellow}SUMMARY${echo_normal} " } -_bash-it-is-partial-match() { - local component="$1" - local term="$2" - _bash-it-component-help "${component}" | $(_bash-it-grep) -E -i -q -- "${term}" +function _bash-it-is-partial-match() { + local component="${1?${FUNCNAME[0]}: component type must be specified}" + local term="${2:-}" + _bash-it-component-help "${component}" | _bash-it-egrep -i -q -- "${term}" } -_bash-it-component-term-matches-negation() { - local match="$1"; shift - local negative - for negative in "$@"; do - [[ "${match}" =~ "${negative}" ]] && return 0 - done +function _bash-it-component-term-matches-negation() { + local match="$1" + shift + local negative + for negative in "$@"; do + [[ "${match}" =~ ${negative} ]] && return 0 + done - return 1 + return 1 } -_bash-it-search-component() { - local component="$1" - shift +function _bash-it-search-component() { + _about 'searches for given terms amongst a given component' + _param '1: component type, one of: [ aliases | plugins | completions ]' + _param '2: term1 term2 @term3' + _param '3: [-]term4 [-]term5 ...' + _example '$ _bash-it-search-component aliases @git rake bundler -chruby' - _about 'searches for given terms amongst a given component' - _param '1: component type, one of: [ aliases | plugins | completions ]' - _param '2: term1 term2 @term3' - _param '3: [-]term4 [-]term5 ...' - _example '$ _bash-it-search-component aliases @git rake bundler -chruby' + local component="${1?${FUNCNAME[0]}: component type must be specified}" + shift - # if one of the search terms is --enable or --disable, we will apply - # this action to the matches further ` down. - local component_singular action action_func - local -a search_commands=(enable disable) - for search_command in "${search_commands[@]}"; do - if $(_bash-it-array-contains-element "--${search_command}" "$@"); then - component_singular=${component} - component_singular=${component_singular/es/} # aliases -> alias - component_singular=${component_singular/ns/n} # plugins -> plugin + # if one of the search terms is --enable or --disable, we will apply + # this action to the matches further ` down. + local component_singular action action_func + local -a search_commands=('enable' 'disable') + for search_command in "${search_commands[@]}"; do + if _bash-it-array-contains-element "--${search_command}" "$@"; then + component_singular="${component/es/}" # aliases -> alias + component_singular="${component_singular/ns/n}" # plugins -> plugin - action="${search_command}" - action_func="_${action}-${component_singular}" - break - fi - done + action="${search_command}" + action_func="_${action}-${component_singular}" + break + fi + done - local -a terms=($@) # passed on the command line + local -a terms=("$@") # passed on the command line - unset exact_terms - unset partial_terms - unset negative_terms + local -a exact_terms=() # terms that should be included only if they match exactly + local -a partial_terms=() # terms that should be included if they match partially + local -a negative_terms=() # negated partial terms that should be excluded - local -a exact_terms=() # terms that should be included only if they match exactly - local -a partial_terms=() # terms that should be included if they match partially - local -a negative_terms=() # negated partial terms that should be excluded + local term line - unset component_list - local -a component_list=( $(_bash-it-component-list "${component}") ) - local term + local -a component_list=() + while IFS='' read -r line; do + component_list+=("$line") + done < <(_bash-it-component-list "${component}") - for term in "${terms[@]}"; do - local search_term="${term:1}" - if [[ "${term:0:2}" == "--" ]] ; then - continue - elif [[ "${term:0:1}" == "-" ]] ; then - negative_terms=(${negative_terms[@]} "${search_term}") - elif [[ "${term:0:1}" == "@" ]] ; then - if $(_bash-it-array-contains-element "${search_term}" "${component_list[@]}"); then - exact_terms=(${exact_terms[@]} "${search_term}") - fi - else - partial_terms=(${partial_terms[@]} $(_bash-it-component-list-matching "${component}" "${term}") ) - fi - done + for term in "${terms[@]}"; do + local search_term="${term:1}" + if [[ "${term:0:2}" == "--" ]]; then + continue + elif [[ "${term:0:1}" == "-" ]]; then + negative_terms+=("${search_term}") + elif [[ "${term:0:1}" == "@" ]]; then + if _bash-it-array-contains-element "${search_term}" "${component_list[@]:-}"; then + exact_terms+=("${search_term}") + fi + else + while IFS='' read -r line; do + partial_terms+=("$line") + done < <(_bash-it-component-list-matching "${component}" "${term}") - local -a total_matches=( $(_bash-it-array-dedup ${exact_terms[@]} ${partial_terms[@]}) ) + fi + done - unset matches - declare -a matches=() - for match in ${total_matches[@]}; do - local include_match=true - if [[ ${#negative_terms[@]} -gt 0 ]]; then - ( _bash-it-component-term-matches-negation "${match}" "${negative_terms[@]}" ) && include_match=false - fi - ( ${include_match} ) && matches=(${matches[@]} "${match}") - done - _bash-it-search-result "${component}" "${action}" "${action_func}" "${matches[@]}" - unset matches final_matches terms + local -a total_matches=() + while IFS='' read -r line; do + total_matches+=("$line") + done < <(_bash-it-array-dedup "${exact_terms[@]:-}" "${partial_terms[@]:-}") + + local -a matches=() + for match in "${total_matches[@]}"; do + local -i include_match=1 + if [[ ${#negative_terms[@]} -gt 0 ]]; then + _bash-it-component-term-matches-negation "${match}" "${negative_terms[@]:-}" && include_match=0 + fi + ((include_match)) && matches+=("${match}") + done + + _bash-it-search-result "${component}" "${action:-}" "${action_func:-}" "${matches[@]:-}" } -_bash-it-search-result() { - local component="$1"; shift - local action="$1"; shift - local action_func="$1"; shift - local -a matches=($@) +function _bash-it-search-result() { + local component="${1?${FUNCNAME[0]}: component type must be specified}" + shift + local action="${1:-}" + shift + local action_func="${1:-}" + shift - local color_component color_enable color_disable color_off + local color_component color_enable color_disable color_off + local match_color compatible_action suffix opposite_suffix + local color_sep=':' line match matched temp + local -i modified=0 enabled=0 len + local -a matches=() - color_sep=':' + # Discard any empty arguments + while IFS='' read -r line; do + [[ -n "${line}" ]] && matches+=("$line") + done < <(_bash-it-array-dedup "${@}") - ( ${BASH_IT_SEARCH_USE_COLOR} ) && { - color_component='\e[1;34m' - color_enable='\e[1;32m' - suffix_enable='' - suffix_disable='' - color_disable='\e[0;0m' - color_off='\e[0;0m' - } + if [[ "${BASH_IT_SEARCH_USE_COLOR}" == "true" ]]; then + color_component='\e[1;34m' + color_enable='\e[1;32m' + suffix_enable='' + suffix_disable='' + color_disable='\e[0;0m' + color_off='\e[0;0m' + else + color_component='' + suffix_enable=' ✓ ︎' + suffix_disable=' ' + color_enable='' + color_disable='' + color_off='' + fi - ( ${BASH_IT_SEARCH_USE_COLOR} ) || { - color_component='' - suffix_enable=' ✓ ︎' - suffix_disable=' ' - color_enable='' - color_disable='' - color_off='' - } + if [[ "${#matches[@]}" -gt 0 ]]; then + printf "${color_component}%13s${color_sep}${color_off} " "${component}" - local match - local modified=0 + for match in "${matches[@]}"; do + enabled=0 + _bash-it-component-item-is-enabled "${component}" "${match}" && enabled=1 - if [[ "${#matches[@]}" -gt 0 ]] ; then - printf "${color_component}%13s${color_sep} ${color_off}" "${component}" + if ((enabled)); then + match_color="${color_enable}" + suffix="${suffix_enable}" + opposite_suffix="${suffix_disable}" + compatible_action="disable" + else + match_color="${color_disable}" + suffix="${suffix_disable}" + opposite_suffix="${suffix_enable}" + compatible_action="enable" + fi - for match in "${matches[@]}"; do - local enabled=0 - ( _bash-it-component-item-is-enabled "${component}" "${match}" ) && enabled=1 + matched="${match}${suffix}" + len="${#matched}" - local match_color compatible_action suffix opposite_suffix + printf '%b' "${match_color}${matched}" # print current state + if [[ "${action}" == "${compatible_action}" ]]; then + if [[ "${action}" == "enable" && "${BASH_IT_SEARCH_USE_COLOR}" == "true" ]]; then + _bash-it-flash-term "${len}" "${matched}" + else + _bash-it-erase-term "${len}" "${matched}" + fi + modified=1 + # shellcheck disable=SC2034 # no idea if `$result` is ever used + result=$("${action_func}" "${match}") + temp="color_${compatible_action}" + match_color="${!temp}" + _bash-it-rewind "${len}" + printf '%b' "${match_color}${match}${opposite_suffix}" + fi - (( ${enabled} )) && { - match_color=${color_enable} - suffix=${suffix_enable} - opposite_suffix=${suffix_disable} - compatible_action="disable" - } + printf '%b' "${color_off} " + done - (( ${enabled} )) || { - match_color=${color_disable} - suffix=${suffix_disable} - opposite_suffix=${suffix_enable} - compatible_action="enable" - } - - local m="${match}${suffix}" - local len - len=${#m} - - printf " ${match_color}${match}${suffix}" # print current state - if [[ "${action}" == "${compatible_action}" ]]; then - if [[ ${action} == "enable" && ${BASH_IT_SEARCH_USE_COLOR} == false ]]; then - _bash-it-flash-term ${len} "${match}${suffix}" - else - _bash-it-erase-term ${len} - fi - modified=1 - result=$(${action_func} ${match}) - local temp="color_${compatible_action}" - match_color=${!temp} - _bash-it-rewind ${len} - printf "${match_color}${match}${opposite_suffix}" - fi - - printf "${color_off}" - done - - [[ ${modified} -gt 0 ]] && _bash-it-clean-component-cache ${component} - printf "\n" - fi + ((modified)) && _bash-it-component-cache-clean "${component}" + printf "\n" + fi } -_bash-it-rewind() { - local len="$1" - printf "\033[${len}D" +function _bash-it-rewind() { + local -i len="${1:-0}" + printf '%b' "\033[${len}D" } -_bash-it-flash-term() { - local len="$1" - local match="$2" - local delay=0.1 - local color +function _bash-it-flash-term() { + local -i len="${1:-0}" # redundant + local term="${2:-}" + # as currently implemented, `$match` has already been printed to screen the first time + local delay=0.2 + local color + [[ "${#term}" -gt 0 ]] && len="${#term}" - for color in ${text_black} ${echo_bold_blue} ${bold_yellow} ${bold_red} ${echo_bold_green} ; do - sleep ${delay} - _bash-it-rewind "${len}" - printf "${color}${match}" - done + for color in "${echo_black-}" "${echo_bold_blue-}" "${echo_bold_yellow-}" "${echo_bold_red-}" "${echo_bold_green-}" "${echo_normal-}"; do + sleep "${delay}" + _bash-it-rewind "${len}" + printf '%b' "${color}${term}" + done } -_bash-it-erase-term() { - local len="$1" - _bash-it-rewind ${len} - for a in {0..30}; do - [[ ${a} -gt ${len} ]] && break - printf "%.*s" $a " " - sleep 0.05 - done +function _bash-it-erase-term() { + local -i len="${1:-0}" i + local delay=0.05 + local term="${2:-}" # calculate length ourselves + [[ "${#term}" -gt 0 ]] && len="${#term}" + + _bash-it-rewind "${len}" + # white-out the already-printed term by printing blanks + for ((i = 0; i <= len; i++)); do + printf "%.*s" "$i" " " + sleep "${delay}" + done } diff --git a/lib/utilities.bash b/lib/utilities.bash old mode 100755 new mode 100644 index b6322a1d..6d5fd5d4 --- a/lib/utilities.bash +++ b/lib/utilities.bash @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +# shellcheck shell=bash # # A collection of reusable functions. @@ -6,22 +6,25 @@ # Generic utilies ########################################################################### -_bash-it-get-component-name-from-path() { - # filename without path - filename=${1##*/} - # filename without path or priority - filename=${filename##*---} - # filename without path, priority or extension - echo ${filename%.*.bash} +function _bash-it-get-component-name-from-path() { + local filename + # filename without path + filename="${1##*/}" + # filename without path or priority + filename="${filename##*"${BASH_IT_LOAD_PRIORITY_SEPARATOR?}"}" + # filename without path, priority or extension + echo "${filename%.*.bash}" } -_bash-it-get-component-type-from-path() { - # filename without path - filename=${1##*/} - # filename without path or priority - filename=${filename##*---} - # extension - echo ${filename} | cut -d '.' -f 2 +function _bash-it-get-component-type-from-path() { + local filename + # filename without path + filename="${1##*/}" + # filename without extension + filename="${filename%.bash}" + # extension without priority or name + filename="${filename##*.}" + echo "${filename}" } # This function searches an array for an exact match against the term passed @@ -37,128 +40,159 @@ _bash-it-get-component-type-from-path() { # $ _bash-it-array-contains-element apple "@{fruits[@]}" && echo 'contains apple' # contains apple # -# $ if $(_bash-it-array-contains-element pear "${fruits[@]}"); then +# $ if _bash-it-array-contains-element pear "${fruits[@]}"; then # echo "contains pear!" # fi # contains pear! # # -_bash-it-array-contains-element() { - local e - for e in "${@:2}"; do - [[ "$e" == "$1" ]] && return 0 - done - return 1 +function _bash-it-array-contains-element() { + local e element="${1?}" + shift + for e in "$@"; do + [[ "$e" == "${element}" ]] && return 0 + done + return 1 } -# Dedupe a simple array of words without spaces. -_bash-it-array-dedup() { - echo "$*" | tr ' ' '\n' | sort -u | tr '\n' ' ' +# Dedupe an array (without embedded newlines). +function _bash-it-array-dedup() { + printf '%s\n' "$@" | sort -u } # Outputs a full path of the grep found on the filesystem -_bash-it-grep() { - if [[ -z "${BASH_IT_GREP:-}" ]] ; then - export BASH_IT_GREP="$(which egrep || which grep || '/usr/bin/grep')" - fi - printf "%s " "${BASH_IT_GREP}" +function _bash-it-grep() { + : "${BASH_IT_GREP:=$(type -P egrep || type -P grep)}" + printf "%s" "${BASH_IT_GREP:-/usr/bin/grep}" } +# Runs `grep` with extended regular expressions +function _bash-it-egrep() { + : "${BASH_IT_GREP:=$(type -P egrep || type -P grep)}" + "${BASH_IT_GREP:-/usr/bin/grep}" -E "$@" +} ########################################################################### # Component-specific functions (component is either an alias, a plugin, or a # completion). ########################################################################### -_bash-it-component-help() { - local component=$(_bash-it-pluralize-component "${1}") - local file=$(_bash-it-component-cache-file ${component}) - if [[ ! -s "${file}" || -z $(find "${file}" -mmin -300) ]] ; then - rm -f "${file}" 2>/dev/null - local func="_bash-it-${component}" - ${func} | $(_bash-it-grep) -E ' \[' | cat > ${file} - fi - cat "${file}" +function _bash-it-component-help() { + local component file func + _bash-it-component-pluralize "${1}" component + _bash-it-component-cache-file "${component}" file + if [[ ! -s "${file?}" || -z "$(find "${file}" -mmin -300)" ]]; then + func="_bash-it-${component?}" + "${func}" | _bash-it-egrep '\[[x ]\]' >| "${file}" + fi + cat "${file}" } -_bash-it-component-cache-file() { - local component=$(_bash-it-pluralize-component "${1}") - local file="${BASH_IT}/tmp/cache/${component}" - [[ -f ${file} ]] || mkdir -p $(dirname ${file}) - printf "${file}" +function _bash-it-component-cache-file() { + local _component_to_cache _file_path _result="${2:-${FUNCNAME[0]//-/_}}" + _bash-it-component-pluralize "${1?${FUNCNAME[0]}: component name required}" _component_to_cache + _file_path="${XDG_CACHE_HOME:-${HOME?}/.cache}/bash/${_component_to_cache?}" + [[ -f "${_file_path}" ]] || mkdir -p "${_file_path%/*}" + printf -v "${_result?}" '%s' "${_file_path}" } -_bash-it-pluralize-component() { - local component="${1}" - local len=$(( ${#component} - 1 )) - # pluralize component name for consistency - [[ ${component:${len}:1} != 's' ]] && component="${component}s" - [[ ${component} == "alias" ]] && component="aliases" - printf ${component} +function _bash-it-component-singularize() { + local _result="${2:-${FUNCNAME[0]//-/_}}" + local _component_to_single="${1?${FUNCNAME[0]}: component name required}" + local -i len="$((${#_component_to_single} - 2))" + if [[ "${_component_to_single:${len}:2}" == 'ns' ]]; then + _component_to_single="${_component_to_single%s}" + elif [[ "${_component_to_single}" == "aliases" ]]; then + _component_to_single="${_component_to_single%es}" + fi + printf -v "${_result?}" '%s' "${_component_to_single}" } -_bash-it-clean-component-cache() { - local component="$1" - local cache - local -a BASH_IT_COMPONENTS=(aliases plugins completions) - if [[ -z ${component} ]] ; then - for component in "${BASH_IT_COMPONENTS[@]}" ; do - _bash-it-clean-component-cache "${component}" - done - else - cache="$(_bash-it-component-cache-file ${component})" - if [[ -f "${cache}" ]] ; then - rm -f "${cache}" - fi - fi +function _bash-it-component-pluralize() { + local _result="${2:-${FUNCNAME[0]//-/_}}" + local _component_to_plural="${1?${FUNCNAME[0]}: component name required}" + local -i len="$((${#_component_to_plural} - 1))" + # pluralize component name for consistency + if [[ "${_component_to_plural:${len}:1}" != 's' ]]; then + _component_to_plural="${_component_to_plural}s" + elif [[ "${_component_to_plural}" == "alias" ]]; then + _component_to_plural="${_component_to_plural}es" + fi + printf -v "${_result?}" '%s' "${_component_to_plural}" +} + +function _bash-it-component-cache-clean() { + local component="${1:-}" + local cache + local -a components=('aliases' 'plugins' 'completions') + if [[ -z "${component}" ]]; then + for component in "${components[@]}"; do + _bash-it-component-cache-clean "${component}" + done + else + _bash-it-component-cache-file "${component}" cache + : >| "${cache:?}" + fi } # Returns an array of items within each compoenent. -_bash-it-component-list() { - local component="$1" - _bash-it-component-help "${component}" | awk '{print $1}' | uniq | sort | tr '\n' ' ' +function _bash-it-component-list() { + local IFS=$'\n' component="$1" + _bash-it-component-help "${component}" | awk '{print $1}' | sort -u } -_bash-it-component-list-matching() { - local component="$1"; shift - local term="$1" - _bash-it-component-help "${component}" | $(_bash-it-grep) -E -- "${term}" | awk '{print $1}' | sort | uniq +function _bash-it-component-list-matching() { + local component="$1" + shift + local term="$1" + _bash-it-component-help "${component}" | _bash-it-egrep -- "${term}" | awk '{print $1}' | sort -u } -_bash-it-component-list-enabled() { - local component="$1" - _bash-it-component-help "${component}" | $(_bash-it-grep) -E '\[x\]' | awk '{print $1}' | uniq | sort | tr '\n' ' ' +function _bash-it-component-list-enabled() { + local IFS=$'\n' component="$1" + _bash-it-component-help "${component}" | _bash-it-egrep '\[x\]' | awk '{print $1}' | sort -u } -_bash-it-component-list-disabled() { - local component="$1" - _bash-it-component-help "${component}" | $(_bash-it-grep) -E -v '\[x\]' | awk '{print $1}' | uniq | sort | tr '\n' ' ' +function _bash-it-component-list-disabled() { + local IFS=$'\n' component="$1" + _bash-it-component-help "${component}" | _bash-it-egrep -v '\[x\]' | awk '{print $1}' | sort -u } # Checks if a given item is enabled for a particular component/file-type. -# Uses the component cache if available. # # Returns: # 0 if an item of the component is enabled, 1 otherwise. # # Examples: # _bash-it-component-item-is-enabled alias git && echo "git alias is enabled" -_bash-it-component-item-is-enabled() { - local component="$1" - local item="$2" - _bash-it-component-help "${component}" | $(_bash-it-grep) -E '\[x\]' | $(_bash-it-grep) -E -q -- "^${item}\s" +function _bash-it-component-item-is-enabled() { + local component_type item_name each_file + + if [[ -f "${1?}" ]]; then + item_name="$(_bash-it-get-component-name-from-path "${1}")" + component_type="$(_bash-it-get-component-type-from-path "${1}")" + else + component_type="${1}" item_name="${2?}" + fi + + for each_file in "${BASH_IT}/enabled"/*"${BASH_IT_LOAD_PRIORITY_SEPARATOR?}${item_name}.${component_type}"*."bash" \ + "${BASH_IT}/${component_type}"*/"enabled/${item_name}.${component_type}"*."bash" \ + "${BASH_IT}/${component_type}"*/"enabled"/*"${BASH_IT_LOAD_PRIORITY_SEPARATOR?}${item_name}.${component_type}"*."bash"; do + if [[ -f "${each_file}" ]]; then + return 0 + fi + done + + return 1 } # Checks if a given item is disabled for a particular component/file-type. -# Uses the component cache if available. # # Returns: # 0 if an item of the component is enabled, 1 otherwise. # # Examples: # _bash-it-component-item-is-disabled alias git && echo "git aliases are disabled" -_bash-it-component-item-is-disabled() { - local component="$1" - local item="$2" - _bash-it-component-help "${component}" | $(_bash-it-grep) -E -v '\[x\]' | $(_bash-it-grep) -E -q -- "^${item}\s" +function _bash-it-component-item-is-disabled() { + ! _bash-it-component-item-is-enabled "$@" } diff --git a/plugins/available/alias-completion.plugin.bash b/plugins/available/alias-completion.plugin.bash index 0db9b04b..d23779f7 100644 --- a/plugins/available/alias-completion.plugin.bash +++ b/plugins/available/alias-completion.plugin.bash @@ -1,106 +1,5 @@ # shellcheck shell=bash -# Load after the other completions to understand what needs to be completed -# BASH_IT_LOAD_PRIORITY: 365 +# stub for renamed file -cite about-plugin -about-plugin 'Automatic completion of aliases' - -# References: -# http://superuser.com/a/437508/119764 -# http://stackoverflow.com/a/1793178/1228454 - -# This needs to be a plugin so it gets executed after the completions and the aliases have been defined. -# Bash-it loads its components in the order -# 1) Aliases -# 2) Completions -# 3) Plugins -# 4) Custom scripts - -# Automatically add completion for all aliases to commands having completion functions -function alias_completion { - local namespace="alias_completion" - - # parse function based completion definitions, where capture group 2 => function and 3 => trigger - local compl_regex='complete( +[^ ]+)* -F ([^ ]+) ("[^"]+"|[^ ]+)' - # parse alias definitions, where capture group 1 => trigger, 2 => command, 3 => command arguments - local alias_regex="alias( -- | )([^=]+)='(\"[^\"]+\"|[^ ]+)(( +[^ ]+)*)'" - - # create array of function completion triggers, keeping multi-word triggers together - eval "local completions=($(complete -p | sed -Ene "/$compl_regex/s//'\3'/p"))" - ((${#completions[@]} == 0)) && return 0 - - # create temporary file for wrapper functions and completions - local tmp_file - tmp_file="$(mktemp -t "${namespace}-${RANDOM}XXXXXX")" || return 1 - - local completion_loader - completion_loader="$(complete -p -D 2> /dev/null | sed -Ene 's/.* -F ([^ ]*).*/\1/p')" - - # read in " '' ''" lines from defined aliases - local line - - # shellcheck disable=SC2162 - # some aliases do have backslashes that needs to be interpreted - while read line; do - eval "local alias_tokens; alias_tokens=($line)" 2> /dev/null || continue # some alias arg patterns cause an eval parse error - local alias_name="${alias_tokens[0]}" alias_cmd="${alias_tokens[1]}" alias_args="${alias_tokens[2]# }" - - # skip aliases to pipes, boolean control structures and other command lists - # (leveraging that eval errs out if $alias_args contains unquoted shell metacharacters) - eval "local alias_arg_words; alias_arg_words=($alias_args)" 2> /dev/null || continue - # avoid expanding wildcards - read -a alias_arg_words <<< "$alias_args" - - # skip alias if there is no completion function triggered by the aliased command - if ! _bash-it-array-contains-element "$alias_cmd" "${completions[@]}"; then - if [[ -n "$completion_loader" ]]; then - # force loading of completions for the aliased command - eval "$completion_loader $alias_cmd" - # 124 means completion loader was successful - [[ $? -eq 124 ]] || continue - completions+=("$alias_cmd") - else - continue - fi - fi - local new_completion="$(complete -p "$alias_cmd" 2> /dev/null)" - - # create a wrapper inserting the alias arguments if any - if [[ -n $alias_args ]]; then - local compl_func="${new_completion/#* -F /}" - compl_func="${compl_func%% *}" - # avoid recursive call loops by ignoring our own functions - if [[ "${compl_func#_"$namespace"::}" == "$compl_func" ]]; then - local compl_wrapper="_${namespace}::${alias_name}" - echo "function $compl_wrapper { - local compl_word=\$2 - local prec_word=\$3 - # check if prec_word is the alias itself. if so, replace it - # with the last word in the unaliased form, i.e., - # alias_cmd + ' ' + alias_args. - if [[ \$COMP_LINE == \"\$prec_word \$compl_word\" ]]; then - prec_word='$alias_cmd $alias_args' - prec_word=\${prec_word#* } - fi - (( COMP_CWORD += ${#alias_arg_words[@]} )) - COMP_WORDS=($alias_cmd $alias_args \${COMP_WORDS[@]:1}) - (( COMP_POINT -= \${#COMP_LINE} )) - COMP_LINE=\${COMP_LINE/$alias_name/$alias_cmd $alias_args} - (( COMP_POINT += \${#COMP_LINE} )) - $compl_func \"$alias_cmd\" \"\$compl_word\" \"\$prec_word\" - }" >> "$tmp_file" - new_completion="${new_completion/ -F $compl_func / -F $compl_wrapper }" - fi - fi - - # replace completion trigger by alias - if [[ -n $new_completion ]]; then - new_completion="${new_completion% *} $alias_name" - echo "$new_completion" >> "$tmp_file" - fi - done < <(alias -p | sed -Ene "s/$alias_regex/\2 '\3' '\4'/p") - # shellcheck source=/dev/null - source "$tmp_file" && command rm -f "$tmp_file" -} - -alias_completion +_enable-completion aliases && _disable-plugin alias-completion +source "${BASH_IT?}/completion/available/aliases.completion.bash" diff --git a/plugins/available/autojump.plugin.bash b/plugins/available/autojump.plugin.bash index 7e6df7fc..3dfa0bca 100644 --- a/plugins/available/autojump.plugin.bash +++ b/plugins/available/autojump.plugin.bash @@ -1,12 +1,14 @@ +# shellcheck shell=bash cite about-plugin about-plugin 'Autojump configuration, see https://github.com/wting/autojump for more details' # Only supports the Homebrew variant, Debian and Arch at the moment. # Feel free to provide a PR to support other install locations +# shellcheck disable=SC1090 if _bash_it_homebrew_check && [[ -s "${BASH_IT_HOMEBREW_PREFIX}/etc/profile.d/autojump.sh" ]]; then - . "${BASH_IT_HOMEBREW_PREFIX}/etc/profile.d/autojump.sh" -elif command -v dpkg &>/dev/null && dpkg -s autojump &>/dev/null ; then - . "$(dpkg-query -S autojump.sh | cut -d' ' -f2)" -elif command -v pacman &>/dev/null && pacman -Q autojump &>/dev/null ; then - . "$(pacman -Ql autojump | grep autojump.sh | cut -d' ' -f2)" + source "${BASH_IT_HOMEBREW_PREFIX}/etc/profile.d/autojump.sh" +elif _command_exists dpkg && dpkg -s autojump &> /dev/null; then + source "$(dpkg-query -S autojump.sh | cut -d' ' -f2)" +elif _command_exists pacman && pacman -Q autojump &> /dev/null; then + source "$(pacman -Ql autojump | grep autojump.sh | cut -d' ' -f2)" fi diff --git a/plugins/available/base.plugin.bash b/plugins/available/base.plugin.bash old mode 100755 new mode 100644 index 8499a2df..1a905163 --- a/plugins/available/base.plugin.bash +++ b/plugins/available/base.plugin.bash @@ -1,216 +1,185 @@ +# shellcheck shell=bash cite about-plugin about-plugin 'miscellaneous tools' -function ips () -{ - about 'display all ip addresses for this host' - group 'base' - if command -v ifconfig &>/dev/null - then - ifconfig | awk '/inet /{ gsub(/addr:/, ""); print $2 }' - elif command -v ip &>/dev/null - then - ip addr | grep -oP 'inet \K[\d.]+' - else - echo "You don't have ifconfig or ip command installed!" - fi +function ips() { + about 'display all ip addresses for this host' + group 'base' + if _command_exists ifconfig; then + ifconfig | awk '/inet /{ gsub(/addr:/, ""); print $2 }' + elif _command_exists ip; then + ip addr | grep -oP 'inet \K[\d.]+' + else + echo "You don't have ifconfig or ip command installed!" + fi } -function down4me () -{ - about 'checks whether a website is down for you, or everybody' - param '1: website url' - example '$ down4me http://www.google.com' - group 'base' - curl -Ls "http://downforeveryoneorjustme.com/$1" | sed '/just you/!d;s/<[^>]*>//g' +function down4me() { + about 'checks whether a website is down for you, or everybody' + param '1: website url' + example '$ down4me http://www.google.com' + group 'base' + curl -Ls "http://downforeveryoneorjustme.com/$1" | sed '/just you/!d;s/<[^>]*>//g' } -function myip () -{ - about 'displays your ip address, as seen by the Internet' - group 'base' - list=("http://myip.dnsomatic.com/" "http://checkip.dyndns.com/" "http://checkip.dyndns.org/") - for url in ${list[*]} - do - res=$(curl -fs "${url}") - if [[ $? -eq 0 ]];then - break; - fi - done - res=$(echo "$res" | grep -Eo '[0-9\.]+') - echo -e "Your public IP is: ${echo_bold_green} $res ${echo_normal}" +function myip() { + about 'displays your ip address, as seen by the Internet' + group 'base' + list=("http://myip.dnsomatic.com/" "http://checkip.dyndns.com/" "http://checkip.dyndns.org/") + for url in "${list[@]}"; do + if res="$(curl -fs "${url}")"; then + break + fi + done + res="$(echo "$res" | grep -Eo '[0-9\.]+')" + echo -e "Your public IP is: ${echo_bold_green-} $res ${echo_normal-}" } -function pickfrom () -{ - about 'picks random line from file' - param '1: filename' - example '$ pickfrom /usr/share/dict/words' - group 'base' - local file=$1 - [[ -z "$file" ]] && reference $FUNCNAME && return - length=$(cat $file | wc -l) - n=$(expr $RANDOM \* $length \/ 32768 + 1) - head -n $n $file | tail -1 +function pickfrom() { + about 'picks random line from file' + param '1: filename' + example '$ pickfrom /usr/share/dict/words' + group 'base' + local file=${1:-} + local -i n=0 length + if [[ ! -r "$file" ]]; then + reference "${FUNCNAME[0]}" && return + fi + length="$(wc -l < "$file")" + n=$((RANDOM * length / 32768 + 1)) + head -n "$n" "$file" | tail -1 } -function passgen () -{ - about 'generates random password from dictionary words' - param 'optional integer length' - param 'if unset, defaults to 4' - example '$ passgen' - example '$ passgen 6' - group 'base' - local i pass length=${1:-4} - pass=$(echo $(for i in $(eval echo "{1..$length}"); do pickfrom /usr/share/dict/words; done)) - echo "With spaces (easier to memorize): $pass" - echo "Without (use this as the password): $(echo $pass | tr -d ' ')" +function passgen() { + about 'generates random password from dictionary words' + param 'optional integer length' + param 'if unset, defaults to 4' + example '$ passgen' + example '$ passgen 6' + group 'base' + local -i i length=${1:-4} + local pass + # shellcheck disable=SC2034 + pass="$(for i in $(eval "echo {1..$length}"); do pickfrom /usr/share/dict/words; done)" + echo "With spaces (easier to memorize): ${pass//$'\n'/ }" + echo "Without spaces (easier to brute force): ${pass//$'\n'/}" } # Create alias pass to passgen when pass isn't installed or # BASH_IT_LEGACY_PASS is true. -if ! command -v pass &>/dev/null || [[ "${BASH_IT_LEGACY_PASS:-}" = true ]] -then - alias pass=passgen +if ! _command_exists pass || [[ "${BASH_IT_LEGACY_PASS:-}" == true ]]; then + alias pass=passgen fi -function pmdown () -{ - about 'preview markdown file in a browser' - param '1: markdown file' - example '$ pmdown README.md' - group 'base' - if command -v markdown &>/dev/null - then - markdown $1 | browser - else - echo "You don't have a markdown command installed!" - fi -} +if _command_exists markdown && _command_exists browser; then + function pmdown() { + about 'preview markdown file in a browser' + param '1: markdown file' + example '$ pmdown README.md' + group 'base' -function mkcd () -{ - about 'make one or more directories and cd into the last one' - param 'one or more directories to create' - example '$ mkcd foo' - example '$ mkcd /tmp/img/photos/large' - example '$ mkcd foo foo1 foo2 fooN' - example '$ mkcd /tmp/img/photos/large /tmp/img/photos/self /tmp/img/photos/Beijing' - group 'base' - mkdir -p -- "$@" && eval cd -- "\"\$$#\"" -} - -function lsgrep () -{ - about 'search through directory contents with grep' - group 'base' - ls | grep "$*" -} - -function quiet () -{ - about 'what *does* this do?' - group 'base' - $* &> /dev/null & -} - -function banish-cookies () -{ - about 'redirect .adobe and .macromedia files to /dev/null' - group 'base' - rm -r ~/.macromedia ~/.adobe - ln -s /dev/null ~/.adobe - ln -s /dev/null ~/.macromedia -} - -function usage () -{ - about 'disk usage per directory, in Mac OS X and Linux' - param '1: directory name' - group 'base' - if [[ "$OSTYPE" == 'darwin'* ]]; then - if [ -n "$1" ]; then - du -hd 1 "$1" - else - du -hd 1 - fi - - elif [[ "$OSTYPE" = 'linux'* ]]; then - if [[ -n "$1" ]]; then - du -h --max-depth=1 "$1" - else - du -h --max-depth=1 - fi - fi -} - -if [[ ! -e "${BASH_IT}/plugins/enabled/todo.plugin.bash" ]] && [[ ! -e "${BASH_IT}/plugins/enabled/*${BASH_IT_LOAD_PRIORITY_SEPARATOR}todo.plugin.bash" ]] -then -# if user has installed todo plugin, skip this... - function t () - { - about 'one thing todo' - param 'if not set, display todo item' - param '1: todo text' - if [[ "$*" == "" ]] ; then - cat ~/.t - else - echo "$*" > ~/.t - fi - } + markdown "${1?}" | browser + } fi -function command_exists () -{ - about 'checks for existence of a command' - param '1: command to check' - example '$ command_exists ls && echo exists' - group 'base' - type "$1" &> /dev/null ; +function mkcd() { + about 'make one or more directories and cd into the last one' + param 'one or more directories to create' + example '$ mkcd foo' + example '$ mkcd /tmp/img/photos/large' + example '$ mkcd foo foo1 foo2 fooN' + example '$ mkcd /tmp/img/photos/large /tmp/img/photos/self /tmp/img/photos/Beijing' + group 'base' + mkdir -p -- "$@" && cd -- "${!#}" || return } -mkiso () -{ - about 'creates iso from current dir in the parent dir (unless defined)' - param '1: ISO name' - param '2: dest/path' - param '3: src/path' - example 'mkiso' - example 'mkiso ISO-Name dest/path src/path' - group 'base' - - if type "mkisofs" > /dev/null; then - [[ -z ${1+x} ]] && local isoname=${PWD##*/} || local isoname=$1 - [[ -z ${2+x} ]] && local destpath=../ || local destpath=$2 - [[ -z ${3+x} ]] && local srcpath=${PWD} || local srcpath=$3 - - if [[ ! -f "${destpath}${isoname}.iso" ]]; then - echo "writing ${isoname}.iso to ${destpath} from ${srcpath}" - mkisofs -V ${isoname} -iso-level 3 -r -o "${destpath}${isoname}.iso" "${srcpath}" - else - echo "${destpath}${isoname}.iso already exists" - fi - else - echo "mkisofs cmd does not exist, please install cdrtools" - fi +# shellcheck disable=SC2010 +function lsgrep() { + about 'search through directory contents with grep' + group 'base' + ls | grep "$@" } +function quiet() { + about 'what *does* this do?' + group 'base' + nohup "$@" &> /dev/null < /dev/null & +} + +function usage() { + about 'disk usage per directory, in Mac OS X and Linux' + param '1: directory name' + group 'base' + case $OSTYPE in + *'darwin'*) + du -hd 1 "$@" + ;; + *'linux'*) + du -h --max-depth=1 "$@" + ;; + esac +} + +function t() { + about 'todo.sh if available, otherwise one thing todo' + param 'if not set, display todo item' + param '1: todo text' + + local todotxt="${XDG_STATE_HOME:-~/.local/state}/bash_it/todo.txt" + + if _bash-it-component-item-is-enabled plugin todo; then + todo.sh "$@" + return + elif [[ ! -f "${todotxt}" && -f ~/.t ]]; then + mv -vn ~/.t "${todotxt}" # Verbose, so the user knows. Don't overwrite, just in case. + fi + + if [[ "$#" -eq 0 ]]; then + cat "${todotxt}" + else + echo "$@" >| "${todotxt}" + fi +} + +if _command_exists mkisofs; then + function mkiso() { + about 'creates iso from current dir in the parent dir (unless defined)' + param '1: ISO name' + param '2: dest/path' + param '3: src/path' + example 'mkiso' + example 'mkiso ISO-Name dest/path src/path' + group 'base' + + local isoname="${1:-${PWD##*/}}" + local destpath="${2:-../}" + local srcpath="${3:-${PWD}}" + + if [[ ! -f "${destpath%/}/${isoname}.iso" ]]; then + echo "writing ${isoname}.iso to ${destpath} from ${srcpath}" + mkisofs -V "${isoname}" -iso-level 3 -r -o "${destpath%/}/${isoname}.iso" "${srcpath}" + else + echo "${destpath%/}/${isoname}.iso already exists" + fi + } +fi + # useful for administrators and configs -function buf () -{ - about 'back up file with timestamp' - param 'filename' - group 'base' - local filename=$1 - local filetime=$(date +%Y%m%d_%H%M%S) - cp -a "${filename}" "${filename}_${filetime}" +function buf() { + about 'back up file with timestamp' + param 'filename' + group 'base' + local filename="${1?}" filetime + filetime=$(date +%Y%m%d_%H%M%S) + cp -a "${filename}" "${filename}_${filetime}" } -function del() { - about 'move files to hidden folder in tmp, that gets cleared on each reboot' - param 'file or folder to be deleted' - example 'del ./file.txt' - group 'base' - mkdir -p /tmp/.trash && mv "$@" /tmp/.trash; -} +if ! _command_exists del; then + function del() { + about 'move files to hidden folder in tmp, that gets cleared on each reboot' + param 'file or folder to be deleted' + example 'del ./file.txt' + group 'base' + mkdir -p /tmp/.trash && mv "$@" /tmp/.trash + } +fi diff --git a/plugins/available/battery.plugin.bash b/plugins/available/battery.plugin.bash index e8e3995f..b38d7f9d 100644 --- a/plugins/available/battery.plugin.bash +++ b/plugins/available/battery.plugin.bash @@ -1,149 +1,130 @@ -cite about-plugin +# shellcheck shell=bash about-plugin 'display info about your battery charge level' -ac_adapter_connected(){ - if _command_exists upower; - then - upower -i $(upower -e | grep -i BAT) | grep 'state' | grep -q 'charging\|fully-charged' - return $? - elif _command_exists acpi; - then - acpi -a | grep -q "on-line" - return $? - elif _command_exists pmset; - then - pmset -g batt | grep -q 'AC Power' - return $? - elif _command_exists ioreg; - then - ioreg -n AppleSmartBattery -r | grep -q '"ExternalConnected" = Yes' - return $? - elif _command_exists WMIC; - then - WMIC Path Win32_Battery Get BatteryStatus /Format:List | grep -q 'BatteryStatus=2' - return $? - fi +function ac_adapter_connected() { + local batteries + if _command_exists upower; then + IFS=$'\n' read -d '' -ra batteries < <(upower -e | grep -i BAT) + upower -i "${batteries[0]:-}" | grep 'state' | grep -q 'charging\|fully-charged' + elif _command_exists acpi; then + acpi -a | grep -q "on-line" + elif _command_exists pmset; then + pmset -g batt | grep -q 'AC Power' + elif _command_exists ioreg; then + ioreg -n AppleSmartBattery -r | grep -q '"ExternalConnected" = Yes' + elif _command_exists WMIC; then + WMIC Path Win32_Battery Get BatteryStatus /Format:List | grep -q 'BatteryStatus=2' + fi } -ac_adapter_disconnected(){ - if _command_exists upower; - then - upower -i $(upower -e | grep -i BAT) | grep 'state' | grep -q 'discharging' - return $? - elif _command_exists acpi; - then - acpi -a | grep -q "off-line" - return $? - elif _command_exists pmset; - then - pmset -g batt | grep -q 'Battery Power' - return $? - elif _command_exists ioreg; - then - ioreg -n AppleSmartBattery -r | grep -q '"ExternalConnected" = No' - return $? - elif _command_exists WMIC; - then - WMIC Path Win32_Battery Get BatteryStatus /Format:List | grep -q 'BatteryStatus=1' - return $? - fi +function ac_adapter_disconnected() { + local batteries + if _command_exists upower; then + IFS=$'\n' read -d '' -ra batteries < <(upower -e | grep -i BAT) + upower -i "${batteries[0]:-}" | grep 'state' | grep -q 'discharging' + elif _command_exists acpi; then + acpi -a | grep -q "off-line" + elif _command_exists pmset; then + pmset -g batt | grep -q 'Battery Power' + elif _command_exists ioreg; then + ioreg -n AppleSmartBattery -r | grep -q '"ExternalConnected" = No' + elif _command_exists WMIC; then + WMIC Path Win32_Battery Get BatteryStatus /Format:List | grep -q 'BatteryStatus=1' + fi } -battery_percentage(){ - about 'displays battery charge as a percentage of full (100%)' - group 'battery' +function battery_percentage() { + about 'displays battery charge as a percentage of full (100%)' + group 'battery' - declare COMMAND_OUTPUT="no" + local command_output batteries - if _command_exists upower; - then - COMMAND_OUTPUT=$(upower --show-info $(upower --enumerate | grep -i BAT) | grep percentage | grep -o "[0-9]\+" | head -1) - elif _command_exists acpi; - then - COMMAND_OUTPUT=$(acpi -b | awk -F, '/,/{gsub(/ /, "", $0); gsub(/%/,"", $0); print $2}' ) - elif _command_exists pmset; - then - COMMAND_OUTPUT=$(pmset -g ps | sed -n 's/.*[[:blank:]]+*\(.*%\).*/\1/p' | grep -o "[0-9]\+" | head -1) - elif _command_exists ioreg; - then - COMMAND_OUTPUT=$(ioreg -n AppleSmartBattery -r | awk '$1~/Capacity/{c[$1]=$3} END{OFMT="%05.2f"; max=c["\"MaxCapacity\""]; print (max>0? 100*c["\"CurrentCapacity\""]/max: "?")}' | grep -o "[0-9]\+" | head -1) - elif _command_exists WMIC; - then - COMMAND_OUTPUT=$(WMIC PATH Win32_Battery Get EstimatedChargeRemaining /Format:List | grep -o '[0-9]\+' | head -1) - else - COMMAND_OUTPUT="no" - fi + if _command_exists upower; then + IFS=$'\n' read -d '' -ra batteries < <(upower -e | grep -i BAT) + command_output="$(upower --show-info "${batteries[0]:-}" | grep percentage | grep -o '[0-9]\+' | head -1)" + elif _command_exists acpi; then + command_output=$(acpi -b | awk -F, '/,/{gsub(/ /, "", $0); gsub(/%/,"", $0); print $2}') + elif _command_exists pmset; then + command_output=$(pmset -g ps | sed -n 's/.*[[:blank:]]+*\(.*%\).*/\1/p' | grep -o '[0-9]\+' | head -1) + elif _command_exists ioreg; then + command_output=$(ioreg -n AppleSmartBattery -r | awk '$1~/Capacity/{c[$1]=$3} END{OFMT="%05.2f"; max=c["\"MaxCapacity\""]; print (max>0? 100*c["\"CurrentCapacity\""]/max: "?")}' | grep -o '[0-9]\+' | head -1) + elif _command_exists WMIC; then + command_output=$(WMIC PATH Win32_Battery Get EstimatedChargeRemaining /Format:List | grep -o '[0-9]\+' | head -1) + else + command_output="no" + fi - if [ "${COMMAND_OUTPUT}" != "no" ]; then - printf "%02d" "${COMMAND_OUTPUT:--1}" - else - echo "${COMMAND_OUTPUT}" - fi + if [[ "${command_output}" != "no" ]]; then + printf "%02d" "${command_output:--1}" + else + echo "${command_output}" + fi } -battery_charge(){ - about 'graphical display of your battery charge' - group 'battery' +function battery_charge() { + about 'graphical display of your battery charge' + group 'battery' - # Full char - local F_C='▸' - # Depleted char - local D_C='▹' - local DEPLETED_COLOR="${normal}" - local FULL_COLOR="${green}" - local HALF_COLOR="${yellow}" - local DANGER_COLOR="${red}" - local BATTERY_OUTPUT="${DEPLETED_COLOR}${D_C}${D_C}${D_C}${D_C}${D_C}" - local BATTERY_PERC=$(battery_percentage) + # Full char + local f_c='▸' + # Depleted char + local d_c='▹' + local depleted_color="${normal?}" + local full_color="${green?}" + local half_color="${yellow?}" + local danger_color="${red?}" + #local battery_output="${depleted_color}${d_c}${d_c}${d_c}${d_c}${d_c}" + local battery_percentage + battery_percentage=$(battery_percentage) - case $BATTERY_PERC in - no) - echo "" - ;; - 9*) - echo "${FULL_COLOR}${F_C}${F_C}${F_C}${F_C}${F_C}${normal}" - ;; - 8*) - echo "${FULL_COLOR}${F_C}${F_C}${F_C}${F_C}${HALF_COLOR}${F_C}${normal}" - ;; - 7*) - echo "${FULL_COLOR}${F_C}${F_C}${F_C}${F_C}${DEPLETED_COLOR}${D_C}${normal}" - ;; - 6*) - echo "${FULL_COLOR}${F_C}${F_C}${F_C}${HALF_COLOR}${F_C}${DEPLETED_COLOR}${D_C}${normal}" - ;; - 5*) - echo "${FULL_COLOR}${F_C}${F_C}${F_C}${DEPLETED_COLOR}${D_C}${D_C}${normal}" - ;; - 4*) - echo "${FULL_COLOR}${F_C}${F_C}${HALF_COLOR}${F_C}${DEPLETED_COLOR}${D_C}${D_C}${normal}" - ;; - 3*) - echo "${FULL_COLOR}${F_C}${F_C}${DEPLETED_COLOR}${D_C}${D_C}${D_C}${normal}" - ;; - 2*) - echo "${FULL_COLOR}${F_C}${HALF_COLOR}${F_C}${DEPLETED_COLOR}${D_C}${D_C}${D_C}${normal}" - ;; - 1*) - echo "${FULL_COLOR}${F_C}${DEPLETED_COLOR}${D_C}${D_C}${D_C}${D_C}${normal}" - ;; - 05) - echo "${DANGER_COLOR}${F_C}${DEPLETED_COLOR}${D_C}${D_C}${D_C}${D_C}${normal}" - ;; - 04) - echo "${DANGER_COLOR}${F_C}${DEPLETED_COLOR}${D_C}${D_C}${D_C}${D_C}${normal}" - ;; - 03) - echo "${DANGER_COLOR}${F_C}${DEPLETED_COLOR}${D_C}${D_C}${D_C}${D_C}${normal}" - ;; - 02) - echo "${DANGER_COLOR}${F_C}${DEPLETED_COLOR}${D_C}${D_C}${D_C}${D_C}${normal}" - ;; - 0*) - echo "${HALF_COLOR}${F_C}${DEPLETED_COLOR}${D_C}${D_C}${D_C}${D_C}${normal}" - ;; - *) - echo "${DANGER_COLOR}UNPLG${normal}" - ;; - esac + case $battery_percentage in + no) + echo "" + ;; + 9*) + echo "${full_color}${f_c}${f_c}${f_c}${f_c}${f_c}${normal?}" + ;; + 8*) + echo "${full_color}${f_c}${f_c}${f_c}${f_c}${half_color}${f_c}${normal?}" + ;; + 7*) + echo "${full_color}${f_c}${f_c}${f_c}${f_c}${depleted_color}${d_c}${normal?}" + ;; + 6*) + echo "${full_color}${f_c}${f_c}${f_c}${half_color}${f_c}${depleted_color}${d_c}${normal?}" + ;; + 5*) + echo "${full_color}${f_c}${f_c}${f_c}${depleted_color}${d_c}${d_c}${normal?}" + ;; + 4*) + echo "${full_color}${f_c}${f_c}${half_color}${f_c}${depleted_color}${d_c}${d_c}${normal?}" + ;; + 3*) + echo "${full_color}${f_c}${f_c}${depleted_color}${d_c}${d_c}${d_c}${normal?}" + ;; + 2*) + echo "${full_color}${f_c}${half_color}${f_c}${depleted_color}${d_c}${d_c}${d_c}${normal?}" + ;; + 1*) + echo "${full_color}${f_c}${depleted_color}${d_c}${d_c}${d_c}${d_c}${normal?}" + ;; + 05) + echo "${danger_color}${f_c}${depleted_color}${d_c}${d_c}${d_c}${d_c}${normal?}" + ;; + 04) + echo "${danger_color}${f_c}${depleted_color}${d_c}${d_c}${d_c}${d_c}${normal?}" + ;; + 03) + echo "${danger_color}${f_c}${depleted_color}${d_c}${d_c}${d_c}${d_c}${normal?}" + ;; + 02) + echo "${danger_color}${f_c}${depleted_color}${d_c}${d_c}${d_c}${d_c}${normal?}" + ;; + 0*) + echo "${half_color}${f_c}${depleted_color}${d_c}${d_c}${d_c}${d_c}${normal?}" + ;; + *) + echo "${danger_color}UNPLG${normal?}" + ;; + esac } diff --git a/plugins/available/blesh.plugin.bash b/plugins/available/blesh.plugin.bash new file mode 100644 index 00000000..6acd19ff --- /dev/null +++ b/plugins/available/blesh.plugin.bash @@ -0,0 +1,19 @@ +# shellcheck shell=bash +cite about-plugin +about-plugin 'load ble.sh, the Bash line editor!' + +if [[ ${BLE_VERSION-} ]]; then + _log_warning "ble.sh is already loaded!" + return +fi + +_bash_it_ble_path=${XDG_DATA_HOME:-$HOME/.local/share}/blesh/ble.sh +if [[ -f $_bash_it_ble_path ]]; then + # shellcheck disable=1090 + source "$_bash_it_ble_path" --attach=prompt +else + _log_error "Could not find ble.sh in $_bash_it_ble_path" + _log_error "Please install using the following command:" + _log_error "git clone https://github.com/akinomyoga/ble.sh && make -C ble.sh install" +fi +unset _bash_it_ble_path diff --git a/plugins/available/cmd-returned-notify.plugin.bash b/plugins/available/cmd-returned-notify.plugin.bash index a3050875..88c07722 100644 --- a/plugins/available/cmd-returned-notify.plugin.bash +++ b/plugins/available/cmd-returned-notify.plugin.bash @@ -2,15 +2,15 @@ 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= +function precmd_return_notification() { + local command_start="${COMMAND_DURATION_START_SECONDS:=0}" + local current_time="${EPOCHREALTIME:-$SECONDS}" + local -i command_duration="$((${current_time%.*} - ${command_start%.*}))" + if [[ "${command_duration}" -gt "${NOTIFY_IF_COMMAND_RETURNS_AFTER:-5}" ]]; then + printf '\a' + fi + return 0 } -preexec_return_notification() { - [ -z "${LAST_COMMAND_TIME}" ] && export LAST_COMMAND_TIME=$(date +%s) -} - -precmd_functions+=(precmd_return_notification) -preexec_functions+=(preexec_return_notification) +safe_append_prompt_command 'precmd_return_notification' +safe_append_preexec '_command_duration_pre_exec' diff --git a/plugins/available/colors.plugin.bash b/plugins/available/colors.plugin.bash new file mode 100644 index 00000000..47f55609 --- /dev/null +++ b/plugins/available/colors.plugin.bash @@ -0,0 +1,197 @@ +# shellcheck shell=bash +# shellcheck disable=SC2005 + +function __() { + echo "$@" +} + +function __make_ansi() { + next=$1 + shift + echo "\[\e[$("__$next" "$@")m\]" +} + +function __make_echo() { + next=$1 + shift + echo "\033[$("__$next" "$@")m" +} + +function __reset() { + next=$1 + shift + out="$("__$next" "$@")" + echo "0${out:+;${out}}" +} + +function __bold() { + next=$1 + shift + out="$("__$next" "$@")" + echo "${out:+${out};}1" +} + +function __faint() { + next=$1 + shift + out="$("__$next" "$@")" + echo "${out:+${out};}2" +} + +function __italic() { + next=$1 + shift + out="$("__$next" "$@")" + echo "${out:+${out};}3" +} + +function __underline() { + next=$1 + shift + out="$("__$next" "$@")" + echo "${out:+${out};}4" +} + +function __negative() { + next=$1 + shift + out="$("__$next" "$@")" + echo "${out:+${out};}7" +} + +function __crossed() { + next=$1 + shift + out="$("__$next" "$@")" + echo "${out:+${out};}8" +} + +function __color_normal_fg() { + echo "3$1" +} + +function __color_normal_bg() { + echo "4$1" +} + +function __color_bright_fg() { + echo "9$1" +} + +function __color_bright_bg() { + echo "10$1" +} + +function __color_black() { + echo "0" +} + +function __color_red() { + echo "1" +} + +function __color_green() { + echo "2" +} + +function __color_yellow() { + echo "3" +} + +function __color_blue() { + echo "4" +} + +function __color_magenta() { + echo "5" +} + +function __color_cyan() { + echo "6" +} + +function __color_white() { + echo "7" +} + +function __color_rgb() { + r=$1 && g=$2 && b=$3 + [[ $r == "$g" && $g == "$b" ]] && echo $((r / 11 + 232)) && return # gray range above 232 + echo "8;5;$(((r * 36 + b * 6 + g) / 51 + 16))" +} + +function __color() { + color="$1" + shift + case "$1" in + fg | bg) + side="$1" + shift + ;; + *) side="fg" ;; + esac + case "$1" in + normal | bright) + mode="$1" + shift + ;; + *) mode=normal ;; + esac + [[ $color == "rgb" ]] && rgb="$1 $2 $3" + shift 3 + + next=$1 + shift + out="$("__$next" "$@")" + echo "$("__color_${mode}_${side}" "$("__color_${color}" "$rgb")")${out:+;${out}}" +} + +function __black() { + echo "$(__color black "$@")" +} + +function __red() { + echo "$(__color red "$@")" +} + +function __green() { + echo "$(__color green "$@")" +} + +function __yellow() { + echo "$(__color yellow "$@")" +} + +function __blue() { + echo "$(__color blue "$@")" +} + +function __magenta() { + echo "$(__color magenta "$@")" +} + +function __cyan() { + echo "$(__color cyan "$@")" +} + +function __white() { + echo "$(__color white "$@")" +} + +function __rgb() { + echo "$(__color rgb "$@")" +} + +function __color_parse() { + next=$1 + shift + echo "$("__$next" "$@")" +} + +function color() { + echo "$(__color_parse make_ansi "$@")" +} + +function echo_color() { + echo "$(__color_parse make_echo "$@")" +} diff --git a/plugins/available/direnv.plugin.bash b/plugins/available/direnv.plugin.bash index 5fd564f5..62788600 100644 --- a/plugins/available/direnv.plugin.bash +++ b/plugins/available/direnv.plugin.bash @@ -1,4 +1,7 @@ +# shellcheck shell=bash cite about-plugin about-plugin 'load direnv, if you are using it: https://direnv.net/' -[ -x "$(which direnv)" ] && eval "$(direnv hook bash)" +if _command_exists direnv; then + eval "$(direnv hook bash)" +fi diff --git a/plugins/available/dirs.plugin.bash b/plugins/available/dirs.plugin.bash index c215f7ec..34468fa0 100644 --- a/plugins/available/dirs.plugin.bash +++ b/plugins/available/dirs.plugin.bash @@ -1,3 +1,4 @@ +# shellcheck shell=bash # Directory stack navigation: # # Add to stack with: pu /path/to/directory @@ -23,7 +24,7 @@ alias 8="pushd +8" alias 9="pushd +9" # Clone this location -alias pc="pushd \$(pwd)" +alias pc='pushd "${PWD}"' # Push new location alias pu="pushd" @@ -32,76 +33,87 @@ alias pu="pushd" alias po="popd" function dirs-help() { - about 'directory navigation alias usage' - group 'dirs' + about 'directory navigation alias usage' + group 'dirs' - echo "Directory Navigation Alias Usage" - echo - echo "Use the power of directory stacking to move" - echo "between several locations with ease." - echo - echo "d : Show directory stack." - echo "po : Remove current location from stack." - echo "pc : Adds current location to stack." - echo "pu : Adds given location to stack." - echo "1 : Change to stack location 1." - echo "2 : Change to stack location 2." - echo "3 : Change to stack location 3." - echo "4 : Change to stack location 4." - echo "5 : Change to stack location 5." - echo "6 : Change to stack location 6." - echo "7 : Change to stack location 7." - echo "8 : Change to stack location 8." - echo "9 : Change to stack location 9." + echo "Directory Navigation Alias Usage" + echo + echo "Use the power of directory stacking to move" + echo "between several locations with ease." + echo + echo "d : Show directory stack." + echo "po : Remove current location from stack." + echo "pc : Adds current location to stack." + echo "pu : Adds given location to stack." + echo "1 : Change to stack location 1." + echo "2 : Change to stack location 2." + echo "3 : Change to stack location 3." + echo "4 : Change to stack location 4." + echo "5 : Change to stack location 5." + echo "6 : Change to stack location 6." + echo "7 : Change to stack location 7." + echo "8 : Change to stack location 8." + echo "9 : Change to stack location 9." } # Add bookmarking functionality # Usage: -if [ ! -f ~/.dirs ]; then # if doesn't exist, create it - touch ~/.dirs +: "${BASH_IT_DIRS_BKS:=${XDG_STATE_HOME:-${HOME}/.local/state}/bash_it/dirs}" +if [[ -f "${BASH_IT_DIRS_BKS?}" ]]; then + # shellcheck disable=SC1090 + source "${BASH_IT_DIRS_BKS?}" +elif [[ -f ~/.dirs ]]; then + mv -vn ~/.dirs "${BASH_IT_DIRS_BKS?}" + # shellcheck disable=SC1090 + source "${BASH_IT_DIRS_BKS?}" else - source ~/.dirs + touch "${BASH_IT_DIRS_BKS?}" fi -alias L='cat ~/.dirs' +alias L='cat "${BASH_IT_DIRS_BKS?}"' # Goes to destination dir, otherwise stay in the dir -G () { - about 'goes to destination dir' - param '1: directory' - example '$ G ..' - group 'dirs' +function G() { + about 'goes to destination dir' + param '1: directory' + example '$ G ..' + group 'dirs' - cd "${1:-$(pwd)}" ; + cd "${1:-${PWD}}" || return } -S () { - about 'save a bookmark' - param '1: bookmark name' - example '$ S mybkmrk' - group 'dirs' +function S() { + about 'save a bookmark' + param '1: bookmark name' + example '$ S mybkmrk' + group 'dirs' - [[ $# -eq 1 ]] || { echo "${FUNCNAME[0]} function requires 1 argument"; return 1; } + [[ $# -eq 1 ]] || { + echo "${FUNCNAME[0]} function requires 1 argument" + return 1 + } - sed "/$@/d" ~/.dirs > ~/.dirs1; - \mv ~/.dirs1 ~/.dirs; - echo "$@"=\"`pwd`\" >> ~/.dirs; - source ~/.dirs ; + sed "/$1/d" "${BASH_IT_DIRS_BKS?}" > "${BASH_IT_DIRS_BKS?}.new" + command mv "${BASH_IT_DIRS_BKS?}.new" "${BASH_IT_DIRS_BKS?}" + echo "$1"=\""${PWD}"\" >> "${BASH_IT_DIRS_BKS?}" + # shellcheck disable=SC1090 + source "${BASH_IT_DIRS_BKS?}" } -R () { - about 'remove a bookmark' - param '1: bookmark name' - example '$ R mybkmrk' - group 'dirs' +function R() { + about 'remove a bookmark' + param '1: bookmark name' + example '$ R mybkmrk' + group 'dirs' - [[ $# -eq 1 ]] || { echo "${FUNCNAME[0]} function requires 1 argument"; return 1; } + [[ $# -eq 1 ]] || { + echo "${FUNCNAME[0]} function requires 1 argument" + return 1 + } - sed "/$@/d" ~/.dirs > ~/.dirs1; - \mv ~/.dirs1 ~/.dirs; + sed "/$1/d" "${BASH_IT_DIRS_BKS?}" > "${BASH_IT_DIRS_BKS?}.new" + command mv "${BASH_IT_DIRS_BKS?}.new" "${BASH_IT_DIRS_BKS?}" } -alias U='source ~/.dirs' # Update bookmark stack -# Set the Bash option so that no '$' is required when using the above facility -shopt -s cdable_vars +alias U='source "${BASH_IT_DIRS_BKS?}"' # Update bookmark stack diff --git a/plugins/available/fzf.plugin.bash b/plugins/available/fzf.plugin.bash index c4f5ea84..21182ac2 100644 --- a/plugins/available/fzf.plugin.bash +++ b/plugins/available/fzf.plugin.bash @@ -4,14 +4,15 @@ cite about-plugin about-plugin 'load fzf, if you are using it' -_command_exists fzf || return - if [ -r ~/.fzf.bash ] ; then source ~/.fzf.bash elif [ -r "${XDG_CONFIG_HOME:-$HOME/.config}"/fzf/fzf.bash ] ; then source "${XDG_CONFIG_HOME:-$HOME/.config}"/fzf/fzf.bash fi +# No need to continue if the command is not present +_command_exists fzf || return + if [ -z ${FZF_DEFAULT_COMMAND+x} ] && _command_exists fd ; then export FZF_DEFAULT_COMMAND='fd --type f' fi diff --git a/plugins/available/gif.plugin.bash b/plugins/available/gif.plugin.bash index 49c36e4c..a04ff5c7 100644 --- a/plugins/available/gif.plugin.bash +++ b/plugins/available/gif.plugin.bash @@ -1,4 +1,4 @@ -cite about-plugin +# shellcheck shell=bash about-plugin 'video to gif, gif to WebM helper functions' # Based loosely on: @@ -12,287 +12,310 @@ about-plugin 'video to gif, gif to WebM helper functions' # Optional: if lossy is not important, Ubuntu has gifsicle packaged for apt-get, instead of giflossy # Optional: gifski (from `brew install gifski` or github.com/ImageOptim/gifski) # for high quality huge files. -function v2gif { - about 'Converts a .mov/.avi/.mp4 file into an into an animated GIF.' - group 'gif' - param '1: MOV/AVI/MP4 file name(s)' - param '2: -w ; Optional: max width in pixels' - param '3: -l ; Optional: extra lossy level for smaller files (80-200 make sense, needs giflossy instead of gifsicle)' - param '4: -h ; Optional: high quality using gifski (installed seperately) - overrides "--lossy" above!' - param '5: -d ; Optional: delete the original video file if succeeded' - param '6: -t ; Optional: Tag the result with quality stamp for comparison use' - param '7: -f ; Optional: Change number of frames per second (default 10 or original FPS if mediainfo installed)' - param '8: -a ; Optional: Alert if resulting file is over kilobytes (default is 5000, 0 turns off)' - param '9: -m ; Optional: Also create a WebM file (will one day replace GIF, Smaller and higher quality than mp4)' - example '$ v2gif foo.mov' - example '$ v2gif foo.mov -w 600' - example '$ v2gif -l 100 -d *.mp4' - example '$ v2gif -dh *.avi' - example '$ v2gif -thw 600 *.avi *.mov' +function v2gif() { + about 'Converts a .mov/.avi/.mp4 file into an into an animated GIF.' + group 'gif' + param '1: MOV/AVI/MP4 file name(s)' + param '2: -w ; Optional: max width in pixels' + param '3: -l ; Optional: extra lossy level for smaller files (80-200 make sense, needs giflossy instead of gifsicle)' + param '4: -h ; Optional: high quality using gifski (installed seperately) - overrides "--lossy" above!' + param '5: -d ; Optional: delete the original video file if succeeded' + param '6: -t ; Optional: Tag the result with quality stamp for comparison use' + param '7: -f ; Optional: Change number of frames per second (default 10 or original FPS if mediainfo installed)' + param '8: -a ; Optional: Alert if resulting file is over kilobytes (default is 5000, 0 turns off)' + param '9: -m ; Optional: Also create a WebM file (will one day replace GIF, Smaller and higher quality than mp4)' + example '$ v2gif foo.mov' + example '$ v2gif foo.mov -w 600' + example '$ v2gif -l 100 -d *.mp4' + example '$ v2gif -dh *.avi' + example '$ v2gif -thw 600 *.avi *.mov' - local convert=$(which convert) ; [[ -x "$convert" ]] || { echo "No convert found!" ; return 2 ;} - local ffmpeg=$(which ffmpeg) ; [[ -x "$ffmpeg" ]] || { echo "No ffmpeg found!" ; return 2 ;} - local mediainfo=$(which mediainfo) ; [[ -x "$mediainfo" ]] || { echo "No mediainfo found!" ; return 2 ;} - local gifsicle=$(which gifsicle) ; [[ -x "$gifsicle" ]] || { echo "No gifsicle found!" ; return 2 ;} - local getopt=$(which getopt) + local convert ffmpeg mediainfo gifsicle getopt args gifski out_size - if [[ "$OSTYPE" == "darwin"* ]] ; then - # Getopt on BSD is incompatible with GNU - getopt=/usr/local/opt/gnu-getopt/bin/getopt - [[ -x "$getopt" ]] || { echo "No GNU-getopt found!" ; return 2 ;} - fi + convert="$(type -p convert)" + [[ -x "$convert" ]] || { + echo "No convert found!" + return 2 + } + ffmpeg="$(type -p ffmpeg)" + [[ -x "$ffmpeg" ]] || { + echo "No ffmpeg found!" + return 2 + } + mediainfo="$(type -p mediainfo)" + [[ -x "$mediainfo" ]] || { + echo "No mediainfo found!" + return 2 + } + gifsicle="$(type -p gifsicle)" + [[ -x "$gifsicle" ]] || { + echo "No gifsicle found!" + return 2 + } + getopt="$(type -p getopt)" - # Parse the options - local args=$($getopt -l "alert:" -l "lossy:" -l "width:" -l del,delete -l high -l tag -l "fps:" -l webm -o "a:l:w:f:dhmt" -- "$@") + if [[ "$OSTYPE" == "darwin"* ]]; then + # Getopt on BSD is incompatible with GNU + getopt=/usr/local/opt/gnu-getopt/bin/getopt + [[ -x "$getopt" ]] || { + echo "No GNU-getopt found!" + return 2 + } + fi - if [ $? -ne 0 ]; then - echo 'Terminating...' >&2 - return 2 - fi + # Parse the options + args=$("$getopt" -l "alert:" -l "lossy:" -l "width:" -l del,delete -l high -l tag -l "fps:" -l webm -o "a:l:w:f:dhmt" -- "$@") || { + echo 'Terminating...' >&2 + return 2 + } - eval set -- "$args" - local use_gifski="" - local opt_del_after="" - local maxsize="" - local lossiness="" - local maxwidthski="" - local giftagopt="" - local giftag="" - local defaultfps=10 - local infps="" - local fps="" - local make_webm="" - local alert=5000 - while [ $# -ge 1 ]; do - case "$1" in - --) - # No more options left. - shift - break - ;; - -d|--del|--delete) - # Delete after - opt_del_after="true" - shift - ;; - -h|--high) - #High Quality, use gifski - local gifski=$(which gifski) ; [[ -x "$gifski" ]] || { echo "No gifski found!" ; return 2 ; } - use_gifski=true - giftag="${giftag}-h" - shift - ;; - -w|--width) - maxsize="-vf scale=$2:-1" - maxwidthski="-W $2" - giftag="${giftag}-w$2" - shift 2 - ;; - -t|--tag) - # mark with a quality tag - giftagopt="true" - shift - ;; - -l|--lossy) - # Use giflossy parameter - lossiness="--lossy=$2" - giftag="${giftag}-l$2" - shift 2 - ;; - -f|--fps) - # select fps - infps="$2" - giftag="${giftag}-f$2" - shift 2 - ;; - -a|--alert) - # set size alert - alert="$2" - shift 2 - ;; - -m|--webm) - # set size alert - make_webm="true" - shift - ;; - esac - done + eval set -- "$args" + local use_gifski="" + local opt_del_after="" + local maxsize="" + local lossiness="" + local maxwidthski="" + local giftagopt="" + local giftag="" + local defaultfps=10 + local infps="" + local fps="" + local make_webm="" + local alert=5000 + while [[ $# -ge 1 ]]; do + case "$1" in + --) + # No more options left. + shift + break + ;; + -d | --del | --delete) + # Delete after + opt_del_after="true" + shift + ;; + -h | --high) + #High Quality, use gifski + gifski="$(type -p gifski)" + [[ -x "$gifski" ]] || { + echo "No gifski found!" + return 2 + } + use_gifski=true + giftag="${giftag}-h" + shift + ;; + -w | --width) + maxsize="-vf scale=$2:-1" + maxwidthski="-W $2" + giftag="${giftag}-w$2" + shift 2 + ;; + -t | --tag) + # mark with a quality tag + giftagopt="true" + shift + ;; + -l | --lossy) + # Use giflossy parameter + lossiness="--lossy=$2" + giftag="${giftag}-l$2" + shift 2 + ;; + -f | --fps) + # select fps + infps="$2" + giftag="${giftag}-f$2" + shift 2 + ;; + -a | --alert) + # set size alert + alert="$2" + shift 2 + ;; + -m | --webm) + # set size alert + make_webm="true" + shift + ;; + esac + done - if [[ -z "$*" ]]; then - echo "$(tput setaf 1)No input files given. Example: v2gif file [file...] [-w ] [-l ] $(tput sgr 0)" - echo "-d/--del/--delete Delete original vid if done suceessfully (and file not over the size limit)" - echo "-h/--high High Quality - use Gifski instead of gifsicle" - echo "-w/--width N Lock maximum gif width to N pixels, resize if necessary" - echo "-t/--tag Add a tag to the output gif describing the options used (useful for comparing several options)" - echo "-l/--lossy N Use the Giflossy parameter for gifsicle (If your version supports it)" - echo "-f/--fps N Override autodetection of incoming vid FPS (useful for downsampling)" - echo "-a/--alert N Alert if over N kilobytes (Defaults to 5000)" - echo "-m/--webm Also create a webm file" - return 1 - fi + if [[ -z "$*" ]]; then + echo "$(tput setaf 1)No input files given. Example: v2gif file [file...] [-w ] [-l ] $(tput sgr 0)" + echo "-d/--del/--delete Delete original vid if done suceessfully (and file not over the size limit)" + echo "-h/--high High Quality - use Gifski instead of gifsicle" + echo "-w/--width N Lock maximum gif width to N pixels, resize if necessary" + echo "-t/--tag Add a tag to the output gif describing the options used (useful for comparing several options)" + echo "-l/--lossy N Use the Giflossy parameter for gifsicle (If your version supports it)" + echo "-f/--fps N Override autodetection of incoming vid FPS (useful for downsampling)" + echo "-a/--alert N Alert if over N kilobytes (Defaults to 5000)" + echo "-m/--webm Also create a webm file" + return 1 + fi - # Prepare the quality tag if requested. - [[ -z "$giftag" ]] && giftag="-default" - [[ -z "$giftagopt" ]] && giftag="" + # Prepare the quality tag if requested. + [[ -z "$giftag" ]] && giftag="-default" + [[ -z "$giftagopt" ]] && giftag="" - for file ; do + for file; do - local output_file="${file%.*}${giftag}.gif" - local del_after=$opt_del_after + local output_file="${file%.*}${giftag}.gif" + local del_after=$opt_del_after - if [[ "$make_webm" ]] ; then - $ffmpeg -loglevel panic -i "$file" \ - -c:v libvpx -crf 4 -threads 0 -an -b:v 2M -auto-alt-ref 0 \ - -quality best -loop 0 "${file%.*}.webm" || return 2 - fi + if [[ -n "$make_webm" ]]; then + $ffmpeg -loglevel panic -i "$file" \ + -c:v libvpx -crf 4 -threads 0 -an -b:v 2M -auto-alt-ref 0 \ + -quality best -loop 0 "${file%.*}.webm" || return 2 + fi - # Set FPS to match the video if possible, otherwise fallback to default. - if [[ "$infps" ]] ; then - fps=$infps - else - fps=$defaultfps - if [[ -x $mediainfo ]] ; then - fps=$($mediainfo "$file" | grep "Frame rate " |sed 's/.*: \([0-9.]\+\) .*/\1/' | head -1) - [[ -z "$fps" ]] && fps=$($mediainfo "$file" | grep "Minimum frame rate" |sed 's/.*: \([0-9.]\+\) .*/\1/' | head -1) - fi - fi + # Set FPS to match the video if possible, otherwise fallback to default. + if [[ -n "$infps" ]]; then + fps=$infps + else + fps=$defaultfps + if [[ -x "$mediainfo" ]]; then + fps=$($mediainfo "$file" | grep "Frame rate " | sed 's/.*: \([0-9.]\+\) .*/\1/' | head -1) + [[ -z "$fps" ]] && fps=$($mediainfo "$file" | grep "Minimum frame rate" | sed 's/.*: \([0-9.]\+\) .*/\1/' | head -1) + fi + fi - echo "$(tput setaf 2)Creating '$output_file' at $fps FPS ...$(tput sgr 0)" + echo "$(tput setaf 2)Creating '$output_file' at $fps FPS ...$(tput sgr 0)" - if [[ "$use_gifski" = "true" ]] ; then - # I trust @pornel to do his own resizing optimization choices - $ffmpeg -loglevel panic -i "$file" -r $fps -vcodec png v2gif-tmp-%05d.png && \ - $gifski v2gif-tmp-*.png $maxwidthski --fps $(printf "%.0f" $fps) -o "$output_file" || return 2 - else - $ffmpeg -loglevel panic -i "$file" $maxsize -r $fps -vcodec png v2gif-tmp-%05d.png && \ - $convert +dither -layers Optimize v2gif-tmp-*.png GIF:- | \ - $gifsicle $lossiness --no-warnings --colors 256 --delay=$(echo "100/$fps"|bc) --loop --optimize=3 --multifile - > "$output_file" || return 2 - fi + if [[ "$use_gifski" = "true" ]]; then + # I trust @pornel to do his own resizing optimization choices + $ffmpeg -loglevel panic -i "$file" -r "$fps" -vcodec png v2gif-tmp-%05d.png \ + && $gifski v2gif-tmp-*.png "$maxwidthski" --fps "$(printf "%.0f" "$fps")" -o "$output_file" || return 2 + else + $ffmpeg -loglevel panic -i "$file" "$maxsize" -r "$fps" -vcodec png v2gif-tmp-%05d.png \ + && $convert +dither -layers Optimize v2gif-tmp-*.png GIF:- \ + | $gifsicle "$lossiness" --no-warnings --colors 256 --delay="$(echo "100/$fps" | bc)" --loop --optimize=3 --multifile - > "$output_file" || return 2 + fi - rm v2gif-tmp-*.png + rm v2gif-tmp-*.png - # Checking if the file is bigger than Twitter likes and warn - if [[ $alert -gt 0 ]] ; then - local out_size=$(wc --bytes < "$output_file") - if [[ $out_size -gt $(( alert * 1000 )) ]] ; then - echo "$(tput setaf 3)Warning: '$output_file' is $((out_size/1000))kb.$(tput sgr 0)" - [[ "$del_after" == "true" ]] && echo "$(tput setaf 3)Warning: Keeping '$file' even though --del requested.$(tput sgr 0)" - del_after="" - fi - fi + # Checking if the file is bigger than Twitter likes and warn + if [[ $alert -gt 0 ]]; then + out_size=$(wc --bytes < "$output_file") + if [[ $out_size -gt $((alert * 1000)) ]]; then + echo "$(tput setaf 3)Warning: '$output_file' is $((out_size / 1000))kb.$(tput sgr 0)" + [[ "$del_after" == "true" ]] && echo "$(tput setaf 3)Warning: Keeping '$file' even though --del requested.$(tput sgr 0)" + del_after="" + fi + fi - [[ "$del_after" = "true" ]] && rm "$file" + [[ "$del_after" = "true" ]] && rm "$file" - done + done - echo "$(tput setaf 2)Done.$(tput sgr 0)" + echo "$(tput setaf 2)Done.$(tput sgr 0)" } function any2webm() { - about 'Converts an movies and Animated GIF files into an into a modern quality WebM video.' - group 'gif' - param '1: GIF/video file name(s)' - param '2: -s ; Optional: set idth and eight in pixels' - param '3: -d ; Optional: delete the original file if succeeded' - param '4: -t ; Optional: Tag the result with quality stamp for comparison use' - param '5: -f ; Optional: Change number of frames per second' - param '6: -b ; Optional: Set Bandwidth (quality/size of resulting file), Defaults to 2M (bits/sec, accepts fractions)"' - param '7: -a ; Optional: Alert if resulting file is over kilobytes (default is 5000, 0 turns off)' - example '$ any2webm foo.gif' - example '$ any2webm *.mov -b 1.5M -s 600x480' + about 'Converts an movies and Animated GIF files into an into a modern quality WebM video.' + group 'gif' + param '1: GIF/video file name(s)' + param '2: -s ; Optional: set idth and eight in pixels' + param '3: -d ; Optional: delete the original file if succeeded' + param '4: -t ; Optional: Tag the result with quality stamp for comparison use' + param '5: -f ; Optional: Change number of frames per second' + param '6: -b ; Optional: Set Bandwidth (quality/size of resulting file), Defaults to 2M (bits/sec, accepts fractions)"' + param '7: -a ; Optional: Alert if resulting file is over kilobytes (default is 5000, 0 turns off)' + example '$ any2webm foo.gif' + example '$ any2webm *.mov -b 1.5M -s 600x480' - # Parse the options - local args=$(getopt -l alert -l "bandwidth:" -l "width:" -l del,delete -l tag -l "fps:" -l webm -o "a:b:w:f:dt" -- "$@") + local args out_size - if [ $? -ne 0 ]; then - echo 'Terminating...' >&2 - return 2 - fi + # Parse the options + args=$(getopt -l alert -l "bandwidth:" -l "width:" -l del,delete -l tag -l "fps:" -l webm -o "a:b:w:f:dt" -- "$@") || { + echo 'Terminating...' >&2 + return 2 + } - eval set -- "$args" - local opt_del_after="" - local size="" - local webmtagopt="" - local webmtag="" - local defaultfps=10 - local fps="" - local bandwidth="2M" - local alert=5000 - while [ $# -ge 1 ]; do - case "$1" in - --) - # No more options left. - shift - break - ;; - -d|--del|--delete) - # Delete after - opt_del_after="true" - shift - ;; - -s|--size) - size="-s $2" - webmtag="${webmtag}-s$2" - shift 2 - ;; - -t|--tag) - # mark with a quality tag - webmtagopt="true" - shift - ;; - -f|--fps) - # select fps - fps="-r $2" - webmtag="${webmtag}-f$2" - shift 2 - ;; - -b|--bandwidth) - # select bandwidth - bandwidth="$2" - webmtag="${webmtag}-b$2" - shift 2 - ;; - -a|--alert) - # set size alert - alert="$2" - shift 2 - ;; - esac - done + eval set -- "$args" + local opt_del_after="" + local size="" + local webmtagopt="" + local webmtag="" + local defaultfps=10 + local fps="" + local bandwidth="2M" + local alert=5000 + while [[ $# -ge 1 ]]; do + case "$1" in + --) + # No more options left. + shift + break + ;; + -d | --del | --delete) + # Delete after + opt_del_after="true" + shift + ;; + -s | --size) + size="-s $2" + webmtag="${webmtag}-s$2" + shift 2 + ;; + -t | --tag) + # mark with a quality tag + webmtagopt="true" + shift + ;; + -f | --fps) + # select fps + fps="-r $2" + webmtag="${webmtag}-f$2" + shift 2 + ;; + -b | --bandwidth) + # select bandwidth + bandwidth="$2" + webmtag="${webmtag}-b$2" + shift 2 + ;; + -a | --alert) + # set size alert + alert="$2" + shift 2 + ;; + esac + done - if [[ -z "$*" ]]; then - echo "$(tput setaf 1)No input files given. Example: any2webm file [file...] [-w ] < $(tput sgr 0)" - return 1 - fi + if [[ -z "$*" ]]; then + echo "$(tput setaf 1)No input files given. Example: any2webm file [file...] [-w ] < $(tput sgr 0)" + return 1 + fi - # Prepare the quality tag if requested. - [[ -z "$webmtag" ]] && webmtag="-default" - [[ -z "$webmtagopt" ]] && webmtag="" + # Prepare the quality tag if requested. + [[ -z "$webmtag" ]] && webmtag="-default" + [[ -z "$webmtagopt" ]] && webmtag="" - for file ; do + for file; do - local output_file="${file%.*}${webmtag}.webm" - local del_after=$opt_del_after + local output_file="${file%.*}${webmtag}.webm" + local del_after=$opt_del_after - echo "$(tput setaf 2)Creating '$output_file' ...$(tput sgr 0)" + echo "$(tput setaf 2)Creating '$output_file' ...$(tput sgr 0)" - $ffmpeg -loglevel panic -i "$file" \ - -c:v libvpx -crf 4 -threads 0 -an -b:v $bandwidth -auto-alt-ref 0 \ - -quality best $fps $size -loop 0 -pix_fmt yuva420p "$output_file" || return 2 + $ffmpeg -loglevel panic -i "$file" \ + -c:v libvpx -crf 4 -threads 0 -an -b:v "$bandwidth" -auto-alt-ref 0 \ + -quality best "$fps" "$size" -loop 0 -pix_fmt yuva420p "$output_file" || return 2 - # Checking if the file is bigger than Twitter likes and warn - if [[ $alert -gt 0 ]] ; then - local out_size=$(wc --bytes < "$output_file") - if [[ $out_size -gt $(( alert * 1000 )) ]] ; then - echo "$(tput setaf 3)Warning: '$output_file' is $((out_size/1000))kb.$(tput sgr 0)" - [[ "$del_after" == "true" ]] && echo "$(tput setaf 3)Warning: Keeping '$file' even though --del requested.$(tput sgr 0)" - del_after="" - fi - fi + # Checking if the file is bigger than Twitter likes and warn + if [[ $alert -gt 0 ]]; then + out_size=$(wc --bytes < "$output_file") + if [[ $out_size -gt $((alert * 1000)) ]]; then + echo "$(tput setaf 3)Warning: '$output_file' is $((out_size / 1000))kb.$(tput sgr 0)" + [[ "$del_after" == "true" ]] && echo "$(tput setaf 3)Warning: Keeping '$file' even though --del requested.$(tput sgr 0)" + del_after="" + fi + fi - [[ "$del_after" = "true" ]] && rm "$file" + [[ "$del_after" = "true" ]] && rm "$file" - done + done - echo "$(tput setaf 2)Done.$(tput sgr 0)" + echo "$(tput setaf 2)Done.$(tput sgr 0)" } diff --git a/plugins/available/git-subrepo.plugin.bash b/plugins/available/git-subrepo.plugin.bash index 085a69b4..6eb8d7c4 100644 --- a/plugins/available/git-subrepo.plugin.bash +++ b/plugins/available/git-subrepo.plugin.bash @@ -1,6 +1,7 @@ -# Load git-subrepo if you are using it, and initialize completions - -cite about-plugin +# shellcheck shell=bash about-plugin 'load git-subrepo if you are using it, and initialize completions' -[[ -e "${GIT_SUBREPO_ROOT:=~/.git-subrepo}/init" ]] && source "$GIT_SUBREPO_ROOT/init" +if [[ -s "${GIT_SUBREPO_ROOT:=$HOME/.git-subrepo}/init" ]]; then + # shellcheck disable=SC1091 + source "$GIT_SUBREPO_ROOT/init" +fi diff --git a/plugins/available/git.plugin.bash b/plugins/available/git.plugin.bash index 6a85e7dc..3a130955 100644 --- a/plugins/available/git.plugin.bash +++ b/plugins/available/git.plugin.bash @@ -2,12 +2,13 @@ cite about-plugin about-plugin 'git helper functions' +# shellcheck disable=SC2016 function git_remote { - about "adds remote $GIT_HOSTING:$1 to current repo" + about 'adds remote $GIT_HOSTING:$1 to current repo' group "git" - echo "Running: git remote add origin ${GIT_HOSTING}:$1.git" - git remote add origin "$GIT_HOSTING:$1".git + echo "Running: git remote add origin ${GIT_HOSTING:?}:$1.git" + git remote add origin "${GIT_HOSTING}:${1}".git } function git_first_push { @@ -24,14 +25,14 @@ function git_pub() { BRANCH=$(git rev-parse --abbrev-ref HEAD) echo "Publishing ${BRANCH} to remote origin" - git push -u origin "$BRANCH" + git push -u origin "${BRANCH}" } function git_revert() { about 'applies changes to HEAD that revert all changes after this commit' group 'git' - git reset "$1" + git reset "${1:?}" git reset --soft "HEAD@{1}" git commit -m "Revert to ${1}" git reset --hard @@ -49,9 +50,7 @@ function git_rollback() { } function commit_exists() { - git rev-list --quiet "$1" - status=$? - if [ $status -ne 0 ]; then + if git rev-list --quiet "${1:?}"; then echo "Commit ${1} does not exist" kill -INT $$ fi @@ -61,7 +60,7 @@ function git_rollback() { while true; do # shellcheck disable=SC2162 read -p "Do you want to keep all changes from rolled back revisions in your working tree? [Y/N]" RESP - case $RESP in + case "${RESP}" in [yY]) echo "Rolling back to commit ${1} with unstaged changes" @@ -87,7 +86,7 @@ function git_rollback() { while true; do # shellcheck disable=SC2162 read -p "WARNING: This will change your history and move the current HEAD back to commit ${1}, continue? [Y/N]" RESP - case $RESP in + case "${RESP}" in [yY]) keep_changes "$1" @@ -134,8 +133,8 @@ function git_info() { # print all remotes and thier details for remote in $(git remote show); do - echo "$remote": - git remote show "$remote" + echo "${remote}": + git remote show "${remote}" echo done @@ -172,32 +171,32 @@ function git_stats { AUTHORS=$(git shortlog -sn --all | cut -f2 | cut -f1 -d' ') LOGOPTS="" if [ "$1" == '-w' ]; then - LOGOPTS="$LOGOPTS -w" + LOGOPTS="${LOGOPTS} -w" shift fi if [ "$1" == '-M' ]; then - LOGOPTS="$LOGOPTS -M" + LOGOPTS="${LOGOPTS} -M" shift fi if [ "$1" == '-C' ]; then - LOGOPTS="$LOGOPTS -C --find-copies-harder" + LOGOPTS="${LOGOPTS} -C --find-copies-harder" shift fi - for a in $AUTHORS; do + for a in ${AUTHORS}; do echo '-------------------' - echo "Statistics for: $a" + echo "Statistics for: ${a}" echo -n "Number of files changed: " # shellcheck disable=SC2086 - git log $LOGOPTS --all --numstat --format="%n" --author="$a" | cut -f3 | sort -iu | wc -l + git log ${LOGOPTS} --all --numstat --format="%n" --author="${a}" | cut -f3 | sort -iu | wc -l echo -n "Number of lines added: " # shellcheck disable=SC2086 - git log $LOGOPTS --all --numstat --format="%n" --author="$a" | cut -f1 | awk '{s+=$1} END {print s}' + git log ${LOGOPTS} --all --numstat --format="%n" --author="${a}" | cut -f1 | awk '{s+=$1} END {print s}' echo -n "Number of lines deleted: " # shellcheck disable=SC2086 - git log $LOGOPTS --all --numstat --format="%n" --author="$a" | cut -f2 | awk '{s+=$1} END {print s}' + git log ${LOGOPTS} --all --numstat --format="%n" --author="${a}" | cut -f2 | awk '{s+=$1} END {print s}' echo -n "Number of merges: " # shellcheck disable=SC2086 - git log $LOGOPTS --all --merges --author="$a" | grep -c '^commit' + git log ${LOGOPTS} --all --merges --author="${a}" | grep -c '^commit' done else echo "you're currently not in a git repository" @@ -212,18 +211,16 @@ function gittowork() { result=$(curl -L "https://www.gitignore.io/api/$1" 2> /dev/null) - if [[ $result =~ ERROR ]]; then + if [[ "${result}" =~ ERROR ]]; then echo "Query '$1' has no match. See a list of possible queries with 'gittowork list'" - elif [[ $1 = list ]]; then - echo "$result" + elif [[ $1 == list ]]; then + echo "${result}" else if [[ -f .gitignore ]]; then - result=$(echo "$result" | grep -v "# Created by http://www.gitignore.io") + result=$(grep -v "# Created by http://www.gitignore.io" <<< "${result}") echo ".gitignore already exists, appending..." - echo "$result" >> .gitignore - else - echo "$result" > .gitignore fi + echo "${result}" >> .gitignore fi } @@ -257,7 +254,7 @@ function gitignore-reload() { fi # Prompt user to commit or stash changes and exit - if [ $err = 1 ]; then + if [[ "${err}" == 1 ]]; then echo >&2 "Please commit or stash them." fi @@ -265,7 +262,7 @@ function gitignore-reload() { # If we're here, then there are no uncommited or unstaged changes dangling around. # Proceed to reload .gitignore - if [ $err = 0 ]; then + if [[ "${err}" == 0 ]]; then # Remove all cached files git rm -r --cached . @@ -290,6 +287,7 @@ function git-changelog() { return 1 fi + # shellcheck disable=SC2155 local NEXT=$(date +%F) if [[ "$2" == "md" ]]; then @@ -298,9 +296,9 @@ function git-changelog() { # shellcheck disable=SC2162 git log "$1" --no-merges --format="%cd" --date=short | sort -u -r | while read DATE; do echo - echo "### $DATE" - git log --no-merges --format=" * (%h) %s by [%an](mailto:%ae)" --since="$DATE 00:00:00" --until="$DATE 24:00:00" - NEXT=$DATE + echo "### ${DATE}" + git log --no-merges --format=" * (%h) %s by [%an](mailto:%ae)" --since="${DATE} 00:00:00" --until="${DATE} 24:00:00" + NEXT=${DATE} done else echo "CHANGELOG $1" @@ -309,9 +307,10 @@ function git-changelog() { # shellcheck disable=SC2162 git log "$1" --no-merges --format="%cd" --date=short | sort -u -r | while read DATE; do echo - echo "[$DATE]" - git log --no-merges --format=" * (%h) %s by %an <%ae>" --since="$DATE 00:00:00" --until="$DATE 24:00:00" - NEXT=$DATE + echo "[${DATE}]" + git log --no-merges --format=" * (%h) %s by %an <%ae>" --since="${DATE} 00:00:00" --until="${DATE} 24:00:00" + # shellcheck disable=SC2034 + NEXT=${DATE} done fi } diff --git a/plugins/available/goenv.plugin.bash b/plugins/available/goenv.plugin.bash index d00fce67..17e4a0ff 100644 --- a/plugins/available/goenv.plugin.bash +++ b/plugins/available/goenv.plugin.bash @@ -30,7 +30,7 @@ eval "$(goenv init - bash)" # If moving to a directory with a goenv version set, reload the shell # to ensure the shell environment matches expectations. _bash-it-goenv-preexec() { - export GOENV_OLD_VERSION="$(goenv version-name)" + GOENV_OLD_VERSION="$(goenv version-name)" } _bash-it-goenv-precmd() { if [[ -n $GOENV_OLD_VERSION ]] && [[ "$GOENV_OLD_VERSION" != "$(goenv version-name)" ]]; then diff --git a/plugins/available/gradle.plugin.bash b/plugins/available/gradle.plugin.bash index 6267bd84..8dec1313 100644 --- a/plugins/available/gradle.plugin.bash +++ b/plugins/available/gradle.plugin.bash @@ -3,19 +3,10 @@ about-plugin 'Add a gw command to use gradle wrapper if present, else use system function gw() { local file="gradlew" - local curr_path="${PWD}" - local result="gradle" + local result - # Search recursively upwards for file. - until [[ "${curr_path}" == "/" ]]; do - if [[ -e "${curr_path}/${file}" ]]; then - result="${curr_path}/${file}" - break - else - curr_path=$(dirname "${curr_path}") - fi - done + result="$(_bash-it-find-in-ancestor "${file}")" # Call gradle - "${result}" $* + "${result:-gradle}" $* } diff --git a/plugins/available/history-eternal.plugin.bash b/plugins/available/history-eternal.plugin.bash new file mode 100644 index 00000000..829868df --- /dev/null +++ b/plugins/available/history-eternal.plugin.bash @@ -0,0 +1,22 @@ +# shellcheck shell=bash +about-plugin 'eternal bash history' + +if [[ ${BASH_VERSINFO[0]} -lt 4 ]] || [[ ${BASH_VERSINFO[0]} -eq 4 && ${BASH_VERSINFO[1]} -lt 3 ]]; then + _log_warning "Bash version 4.3 introduced the 'unlimited' history size capability." + return 1 +fi + +# Modify history sizes before changing location to avoid unintentionally +# truncating the history file early. + +# "Numeric values less than zero result in every command being saved on the history list (there is no limit)" +readonly HISTSIZE=-1 2> /dev/null || true + +# "Non-numeric values and numeric values less than zero inhibit truncation" +readonly HISTFILESIZE='unlimited' 2> /dev/null || true + +# Use a custom history file location so history is not truncated +# if the environment ever loses this "eternal" configuration. +HISTDIR="${XDG_STATE_HOME:-${HOME?}/.local/state}/bash" +[[ -d ${HISTDIR?} ]] || mkdir -p "${HISTDIR?}" +readonly HISTFILE="${HISTDIR?}/history" 2> /dev/null || true diff --git a/plugins/available/history-search.plugin.bash b/plugins/available/history-search.plugin.bash index ea02eb74..96941993 100644 --- a/plugins/available/history-search.plugin.bash +++ b/plugins/available/history-search.plugin.bash @@ -1,5 +1,4 @@ # shellcheck shell=bash -cite about-plugin about-plugin 'search history using the prefix already entered' # enter a few characters and press UpArrow/DownArrow diff --git a/plugins/available/history-substring-search.plugin.bash b/plugins/available/history-substring-search.plugin.bash index e0e37f43..dde32720 100644 --- a/plugins/available/history-substring-search.plugin.bash +++ b/plugins/available/history-substring-search.plugin.bash @@ -1,5 +1,4 @@ # shellcheck shell=bash -cite about-plugin about-plugin 'search history using the substring already entered' # enter a few characters and press UpArrow/DownArrow diff --git a/plugins/available/history.plugin.bash b/plugins/available/history.plugin.bash index 08ca8de6..d9e930c3 100644 --- a/plugins/available/history.plugin.bash +++ b/plugins/available/history.plugin.bash @@ -1,29 +1,26 @@ # shellcheck shell=bash -cite about-plugin about-plugin 'improve history handling with sane defaults' -# append to bash_history if Terminal.app quits +# Append the history list to the file named by the value of the HISTFILE +# variable when the shell exits, rather than overwriting the file. shopt -s histappend -# erase duplicates; alternative option: export HISTCONTROL=ignoredups -export HISTCONTROL=${HISTCONTROL:-ignorespace:erasedups} +# 'ignorespace': don't save command lines which begin with a space to history +# 'erasedups' (alternative 'ignoredups'): don't save duplicates to history +# 'autoshare': automatically share history between multiple running shells +: "${HISTCONTROL:=ignorespace:erasedups:autoshare}" # resize history to 100x the default (500) -export HISTSIZE=${HISTSIZE:-50000} +: "${HISTSIZE:=50000}" -top-history() { +function top-history() { about 'print the name and count of the most commonly run tools' - if [[ -n $HISTTIMEFORMAT ]]; then - # To parse history we need a predictable format, which HISTTIMEFORMAT - # gets in the way of. So we unset it and set a trap to guarantee the - # user's environment returns to normal even if the pipeline below fails. - # shellcheck disable=SC2064 - trap "export HISTTIMEFORMAT='$HISTTIMEFORMAT'" RETURN - unset HISTTIMEFORMAT - fi - - history \ + # - Make sure formatting doesn't interfer with our parsing + # - Use awk to count how many times the first command on each line has been called + # - Truncate to 10 lines + # - Print in column format + HISTTIMEFORMAT='' history \ | awk '{ a[$2]++ }END{ diff --git a/plugins/available/hub.plugin.bash b/plugins/available/hub.plugin.bash index 0a67a7af..e9a8cbab 100644 --- a/plugins/available/hub.plugin.bash +++ b/plugins/available/hub.plugin.bash @@ -1,4 +1,7 @@ +# shellcheck shell=bash cite about-plugin about-plugin 'load hub, if you are using it' -command -v hub &> /dev/null && eval "$(hub alias -s)" +if _command_exists hub; then + eval "$(hub alias -s)" +fi diff --git a/plugins/available/java.plugin.bash b/plugins/available/java.plugin.bash index 98e46247..2a80a99a 100644 --- a/plugins/available/java.plugin.bash +++ b/plugins/available/java.plugin.bash @@ -1,11 +1,11 @@ -cite about-plugin +# shellcheck shell=bash about-plugin 'Java and JAR helper functions' function jar_manifest { - about "extracts the specified JAR file's MANIFEST file and prints it to stdout" - group 'java' - param '1: JAR file to extract the MANIFEST from' - example 'jar_manifest lib/foo.jar' + about "extracts the specified JAR file's MANIFEST file and prints it to stdout" + group 'java' + param '1: JAR file to extract the MANIFEST from' + example 'jar_manifest lib/foo.jar' - unzip -c $1 META-INF/MANIFEST.MF + unzip -c "${1:?${FUNCNAME[0]}: JAR file must be specified}" META-INF/MANIFEST.MF } diff --git a/plugins/available/jekyll.plugin.bash b/plugins/available/jekyll.plugin.bash index 6254a87f..d818b076 100644 --- a/plugins/available/jekyll.plugin.bash +++ b/plugins/available/jekyll.plugin.bash @@ -1,367 +1,288 @@ +# shellcheck shell=bash cite about-plugin about-plugin 'manage your jekyll site' -editpost() { - about 'edit a post' - param '1: site directory' - group 'jekyll' +function editpost() { + about 'edit a post' + param '1: site directory' + group 'jekyll' - unset SITE - if [ -z "$1" ] - then - echo "Error: no site specified." - echo "The site is the name of the directory your project is in." - return 1 - fi + local SITE site POST DATE TITLE POSTS + local -i COUNTER=1 POST_TO_EDIT ret + if [[ -z "${1:-}" ]]; then + echo "Error: no site specified." + echo "The site is the name of the directory your project is in." + return 1 + fi - for site in ${SITES[@]} - do - if [ "$(basename $site)" = "$1" ] - then - SITE=$site - break - fi - done + for site in "${SITES[@]:-}"; do + if [[ "${site##*/}" == "$1" ]]; then + SITE="${site}" + break + fi + done - if [ -z "$SITE" ] - then - echo "No such site." - return 1 - fi + if [[ -z "${SITE:-}" ]]; then + echo "No such site." + return 1 + fi - builtin cd "$SITE/_posts" + pushd "${SITE}/_posts" > /dev/null || return - COUNTER=1 - NUMBER="$RANDOM" - TMPFILE="/tmp/editpost-$NUMBER" - - for POST in * - do - DATE=`echo $POST | grep -oE "[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}"` - TITLE=`cat $POST | grep -oE "title: (.+)"` - TITLE=`echo $TITLE | sed 's/title: //'` - echo "$COUNTER) $DATE $TITLE" >> "$TMPFILE" - POSTS[$COUNTER]=$POST - COUNTER=`expr $COUNTER + 1` - done - less $TMPFILE - read -p "Number of post to edit: " POST_TO_EDIT - if [ -z "$JEKYLL_EDITOR" ] - then - nano "${POSTS[$POST_TO_EDIT]}" - else - "$JEKYLL_EDITOR" "${POSTS[$POST_TO_EDIT]}" - fi + for POST in *; do + DATE="$(echo "${POST}" | grep -oE "[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}")" + TITLE="$(grep -oE "title: (.+)" < "${POST}")" + TITLE="${TITLE/title: /}" + echo "${COUNTER}) ${DATE} ${TITLE}" + POSTS[COUNTER]="$POST" + COUNTER="$((COUNTER + 1))" + done > >(less) + read -rp "Number of post to edit: " POST_TO_EDIT + "${JEKYLL_EDITOR:-${VISUAL:-${EDITOR:-${ALTERNATE_EDITOR:-nano}}}}" "${POSTS[POST_TO_EDIT]}" + ret="$?" + popd > /dev/null || return "$ret" + return "$ret" } -newpost() { - about 'create a new post' - param '1: site directory' - group 'jekyll' +function newpost() { + about 'create a new post' + param '1: site directory' + group 'jekyll' - unset SITE - if [ -z "$1" ] - then - echo "Error: no site specified." - echo "The site is the name of the directory your project is in." - return 1 - fi + local SITE site FNAME_POST_TITLE FNAME YAML_DATE + local JEKYLL_FORMATTING FNAME_DATE OPTIONS OPTION POST_TYPE POST_TITLE + local -i loc=0 ret + if [[ -z "${1:-}" ]]; then + echo "Error: no site specified." + echo "The site is the name of the directory your project is in." + return 1 + fi - if [ -z "$SITE" ] - then - echo "No such site." - return 1 - fi + if [[ -z "${SITE}" ]]; then + echo "No such site." + return 1 + fi - loc=0 + for site in "${SITES[@]}"; do + if [[ "${site##*/}" == "$1" ]]; then + SITE="$site" + JEKYLL_FORMATTING="${MARKUPS[loc]}" + break + fi + loc=$((loc + 1)) + done - for site in ${SITES[@]} - do - if [ "$(basename $site)" = "$1" ] - then - SITE=$site - JEKYLL_FORMATTING=${MARKUPS[$loc]} - break - fi - loc=$(($loc+1)) - done + # Change directory into the local jekyll root + pushd "${SITE}/_posts" > /dev/null || return - # 'builtin cd' into the local jekyll root + # Get the date for the new post's filename + FNAME_DATE="$(date "+%Y-%m-%d")" - builtin cd "$SITE/_posts" + # If the user is using markdown or textile formatting, let them choose what type of post they want. Sort of like Tumblr. + OPTIONS=('Text' 'Quote' 'Image' 'Audio' 'Video' 'Link') - # Get the date for the new post's filename + if [[ $JEKYLL_FORMATTING == "markdown" || $JEKYLL_FORMATTING == "textile" ]]; then + select OPTION in "${OPTIONS[@]}"; do + POST_TYPE="${OPTION}" + break + done + fi - FNAME_DATE=$(date "+%Y-%m-%d") + # Get the title for the new post + read -rp "Enter title of the new post: " POST_TITLE - # If the user is using markdown or textile formatting, let them choose what type of post they want. Sort of like Tumblr. + # Convert the spaces in the title to hyphens for use in the filename + FNAME_POST_TITLE="${POST_TITLE/ /-}" - OPTIONS="Text Quote Image Audio Video Link" + # Now, put it all together for the full filename + FNAME="$FNAME_DATE-$FNAME_POST_TITLE.$JEKYLL_FORMATTING" - if [ $JEKYLL_FORMATTING = "markdown" -o $JEKYLL_FORMATTING = "textile" ] - then - select OPTION in $OPTIONS - do - if [[ $OPTION = "Text" ]] - then - POST_TYPE="Text" - break - fi + # And, finally, create the actual post file. But we're not done yet... + { + # Write a little stuff to the file for the YAML Front Matter + echo "---" - if [[ $OPTION = "Quote" ]] - then - POST_TYPE="Quote" - break - fi + # Now we have to get the date, again. But this time for in the header (YAML Front Matter) of the file + YAML_DATE="$(date "+%B %d %Y %X")" - if [[ $OPTION = "Image" ]] - then - POST_TYPE="Image" - break - fi + # Echo the YAML Formatted date to the post file + echo "date: $YAML_DATE" - if [[ $OPTION = "Audio" ]] - then - POST_TYPE="Audio" - break - fi + # Echo the original post title to the YAML Front Matter header + echo "title: $POST_TITLE" - if [[ $OPTION = "Video" ]] - then - POST_TYPE="Video" - break - fi + # And, now, echo the "post" layout to the YAML Front Matter header + echo "layout: post" - if [[ $OPTION = "Link" ]] - then - POST_TYPE="Link" - break - fi - done - fi + # Close the YAML Front Matter Header + echo "---" - # Get the title for the new post + echo + } > "${FNAME}" - read -p "Enter title of the new post: " POST_TITLE + # Generate template text based on the post type + if [[ $JEKYLL_FORMATTING == "markdown" ]]; then + case $POST_TYPE in + "Text") + true + ;; + "Quote") + echo "> Quote" + echo + echo "— Author" + ;; + "Image") + echo "![Alternate Text](/path/to/image/or/url)" + ;; + "Audio") + echo "" + ;; + "Video") + echo "" + ;; + "Link") + echo "[link][1]" + echo + echo "> Quote" + echo + echo "[1]: url" + ;; + esac + elif [[ $JEKYLL_FORMATTING == "textile" ]]; then + case $POST_TYPE in + "Text") + true + ;; + "Quote") + echo "bq. Quote" + echo + echo "— Author" + ;; + "Image") + echo "!url(alt text)" + ;; + "Audio") + echo "" + ;; + "Video") + echo "" + ;; + "Link") + echo "\"Site\":url" + echo + echo "bq. Quote" + ;; + esac + fi >> "${FNAME}" - # Convert the spaces in the title to hyphens for use in the filename - - FNAME_POST_TITLE=`echo $POST_TITLE | tr ' ' "-"` - - # Now, put it all together for the full filename - - FNAME="$FNAME_DATE-$FNAME_POST_TITLE.$JEKYLL_FORMATTING" - - # And, finally, create the actual post file. But we're not done yet... - - touch "$FNAME" - - # Write a little stuff to the file for the YAML Front Matter - - echo "---" >> $FNAME - - # Now we have to get the date, again. But this time for in the header (YAML Front Matter) of - # the file - - YAML_DATE=$(date "+%B %d %Y %X") - - # Echo the YAML Formatted date to the post file - - echo "date: $YAML_DATE" >> $FNAME - - # Echo the original post title to the YAML Front Matter header - - echo "title: $POST_TITLE" >> $FNAME - - # And, now, echo the "post" layout to the YAML Front Matter header - - echo "layout: post" >> $FNAME - - # Close the YAML Front Matter Header - - echo "---" >> $FNAME - echo >> $FNAME - - # Generate template text based on the post type - - if [[ $JEKYLL_FORMATTING = "markdown" ]] - then - if [[ $POST_TYPE = "Text" ]] - then - true - fi - - if [[ $POST_TYPE = "Quote" ]] - then - echo "> Quote" >> $FNAME - echo >> $FNAME - echo "— Author" >> $FNAME - fi - - if [[ $POST_TYPE = "Image" ]] - then - echo "![Alternate Text](/path/to/image/or/url)" >> $FNAME - fi - - if [[ $POST_TYPE = "Audio" ]] - then - echo "" >> $FNAME - fi - - if [[ $POST_TYPE = "Video" ]] - then - echo "" >> $FNAME - fi - - if [[ $POST_TYPE = "Link" ]] - then - echo "[link][1]" >> $FNAME - echo >> $FNAME - echo "> Quote" >> $FNAME - echo >> $FNAME - echo "[1]: url" >> $FNAME - fi - fi - - if [[ $JEKYLL_FORMATTING = "textile" ]] - then - if [[ $POST_TYPE = "Text" ]] - then - true - fi - - if [[ $POST_TYPE = "Quote" ]] - then - echo "bq. Quote" >> $FNAME - echo >> $FNAME - echo "— Author" >> $FNAME - fi - - if [[ $POST_TYPE = "Image" ]] - then - echo "!url(alt text)" >> $FNAME - fi - - if [[ $POST_TYPE = "Audio" ]] - then - echo "" >> $FNAME - fi - - if [[ $POST_TYPE = "Video" ]] - then - echo "" >> $FNAME - fi - - if [[ $POST_TYPE = "Link" ]] - then - echo "\"Site\":url" >> $FNAME - echo >> $FNAME - echo "bq. Quote" >> $FNAME - fi - fi - - # Open the file in your favorite editor - - "$JEKYLL_EDITOR" $FNAME + # Open the file in your favorite editor + "${JEKYLL_EDITOR:-${VISUAL:-${EDITOR:-${ALTERNATE_EDITOR:-nano}}}}" "${FNAME}" + ret="$?" + popd > /dev/null || return "$ret" + return "$ret" } function testsite() { - about 'launches local jekyll server' - param '1: site directory' - group 'jekyll' + about 'launches local jekyll server' + param '1: site directory' + group 'jekyll' - unset SITE - if [ -z "$1" ] - then - echo "Error: no site specified." - echo "The site is the name of the directory your project is in." - return 1 - fi + local SITE site + local -i ret + if [[ -z "${1:-}" ]]; then + echo "Error: no site specified." + echo "The site is the name of the directory your project is in." + return 1 + fi - for site in ${SITES[@]} - do - if [ "$(basename $site)" = "$1" ] - then - SITE=$site - break - fi - done + for site in "${SITES[@]}"; do + if [[ "${site##*/}" == "$1" ]]; then + SITE="$site" + break + fi + done - if [ -z "$SITE" ] - then - echo "No such site." - return 1 - fi + if [[ -z "${SITE}" ]]; then + echo "No such site." + return 1 + fi - builtin cd $SITE - jekyll --server --auto + pushd "${SITE}" > /dev/null || return + jekyll --server --auto + ret="$?" + popd > /dev/null || return "$ret" + return "$ret" } function buildsite() { - about 'builds site' - param '1: site directory' - group 'jekyll' + about 'builds site' + param '1: site directory' + group 'jekyll' - unset SITE - if [ -z "$1" ] - then - echo "Error: no site specified." - echo "The site is the name of the directory your project is in." - return 1 - fi + local SITE site + local -i ret + if [[ -z "${1:-}" ]]; then + echo "Error: no site specified." + echo "The site is the name of the directory your project is in." + return 1 + fi - for site in ${SITES[@]} - do - if [ "$(basename $site)" = "$1" ] - then - SITE=$site - break - fi - done + for site in "${SITES[@]}"; do + if [[ "${site##*/}" == "$1" ]]; then + SITE="$site" + break + fi + done - if [ -z "$SITE" ] - then - echo "No such site." - return 1 - fi + if [[ -z "${SITE}" ]]; then + echo "No such site." + return 1 + fi - builtin cd $SITE - rm -rf _site - jekyll --no-server + pushd "${SITE}" > /dev/null || return + rm -rf _site + jekyll --no-server + ret="$?" + popd > /dev/null || return "$ret" + return "$ret" } function deploysite() { - about 'rsyncs site to remote host' - param '1: site directory' - group 'jekyll' + about 'rsyncs site to remote host' + param '1: site directory' + group 'jekyll' - unset SITE - if [ -z "$1" ] - then - echo "Error: no site specified." - echo "The site is the name of the directory your project is in." - return 1 - fi + local SITE site REMOTE + local -i loc=0 ret + if [[ -z "${1:-}" ]]; then + echo "Error: no site specified." + echo "The site is the name of the directory your project is in." + return 1 + fi - loc=0 + for site in "${SITES[@]}"; do + if [[ "${site##*/}" == "$1" ]]; then + SITE="$site" + # shellcheck disable=SC2153 # who knows + REMOTE="${REMOTES[loc]}" + break + fi + loc=$((loc + 1)) + done - for site in ${SITES[@]} - do - if [ "$(basename $site)" = "$1" ] - then - SITE=$site - REMOTE=${REMOTES[$loc]} - break - fi - loc=$(($loc+1)) - done + if [[ -z "${SITE}" ]]; then + echo "No such site." + return 1 + fi - if [ -z "$SITE" ] - then - echo "No such site." - return 1 - fi - - builtin cd $SITE - rsync -rz $REMOTE + pushd "${SITE}" > /dev/null || return + rsync -rz "${REMOTE?}" + ret="$?" + popd > /dev/null || return "$ret" + return "$ret" } + +# Load the Jekyll config +if [[ -s "$HOME/.jekyllconfig" ]]; then + source "$HOME/.jekyllconfig" +fi diff --git a/plugins/available/jump.plugin.bash b/plugins/available/jump.plugin.bash old mode 100755 new mode 100644 index 26d6467d..1713d1b7 --- a/plugins/available/jump.plugin.bash +++ b/plugins/available/jump.plugin.bash @@ -1,9 +1,12 @@ +# shellcheck shell=bash +# shellcheck disable=SC2016 cite about-plugin about-plugin 'initialize jump (see https://github.com/gsamokovarov/jump). Add `export JUMP_OPTS=("--bind=z")` to change keybinding' -__init_jump() { - command -v jump &> /dev/null || return - eval "$(jump shell bash "${JUMP_OPTS[@]}")" +function __init_jump() { + if _command_exists jump; then + eval "$(jump shell bash "${JUMP_OPTS[@]}")" + fi } __init_jump diff --git a/plugins/available/latex.plugin.bash b/plugins/available/latex.plugin.bash index 6ebb70d3..474f4abc 100644 --- a/plugins/available/latex.plugin.bash +++ b/plugins/available/latex.plugin.bash @@ -1,9 +1,19 @@ -cite about-plugin -about-plugin 'use mactex' +# shellcheck shell=bash +about-plugin 'add MacTeX to PATH' + +_bash_it_plugin_latex_paths=( + # Standard locations + /usr/local/texbin + # MacOS locations + /Library/TeX/texbin +) # add mactex to the path if its present -MACTEX_PATH=/usr/local/texlive/2009/bin/universal-darwin -if [[ -d $MACTEX_PATH ]]; then - pathmunge $MACTEX_PATH after -fi -unset MACTEX_PATH +for _bash_it_plugin_latex_path in "${_bash_it_plugin_latex_paths[@]}"; do + if [[ -d "$_bash_it_plugin_latex_path/" ]]; then + pathmunge "$_bash_it_plugin_latex_path" after && break + fi +done + +# Cleanup +unset "${!_bash_it_plugin_latex_@}" diff --git a/plugins/available/less-pretty-cat.plugin.bash b/plugins/available/less-pretty-cat.plugin.bash index b6b9d1f0..139e5188 100644 --- a/plugins/available/less-pretty-cat.plugin.bash +++ b/plugins/available/less-pretty-cat.plugin.bash @@ -1,31 +1,23 @@ +# shellcheck shell=bash cite about-plugin about-plugin 'pygmentize instead of cat to terminal if possible' -if $(command -v pygmentize &> /dev/null) ; then - # get the full paths to binaries - CAT_BIN=$(which cat) - LESS_BIN=$(which less) - BASH_IT_CCAT_STYLE="${BASH_IT_CCAT_STYLE:=default}" - BASH_IT_CLESS_STYLE="${BASH_IT_CLESS_STYLE:=default}" +_command_exists pygmentize || return - # pigmentize cat and less outputs - call them ccat and cless to avoid that - # especially cat'ed output in scripts gets mangled with pygemtized meta characters - function ccat() - { - about 'runs either pygmentize or cat on each file passed in' - param '*: files to concatenate (as normally passed to cat)' - example 'cat mysite/manage.py dir/text-file.txt' - for var; - do - pygmentize -f 256 -O style="$BASH_IT_CCAT_STYLE" -g "$var" 2>/dev/null || "$CAT_BIN" "$var"; - done - } +# pigmentize cat and less outputs - call them ccat and cless to avoid that +# especially cat'ed output in scripts gets mangled with pygemtized meta characters +function ccat() { + about 'runs pygmentize on each file passed in' + param '*: files to concatenate (as normally passed to cat)' + example 'ccat mysite/manage.py dir/text-file.txt' - function cless() - { - about 'it pigments the file passed in and passes it to less for pagination' - param '$1: the file to paginate with less' - example 'less mysite/manage.py' - pygmentize -f 256 -O style="$BASH_IT_CLESS_STYLE" -g $* | "$LESS_BIN" -R - } -fi + pygmentize -f 256 -O style="${BASH_IT_CCAT_STYLE:-default}" -g "$@" +} + +function cless() { + about 'pigments the files passed in and passes to less for pagination' + param '*: the files to paginate with less' + example 'cless mysite/manage.py' + + pygmentize -f 256 -O style="${BASH_IT_CLESS_STYLE:-default}" -g "$@" | command less -R +} diff --git a/plugins/available/man.plugin.bash b/plugins/available/man.plugin.bash index 473f04fd..b981565f 100644 --- a/plugins/available/man.plugin.bash +++ b/plugins/available/man.plugin.bash @@ -1,10 +1,14 @@ -cite about-plugin +# shellcheck shell=bash about-plugin 'colorize man pages for better readability' -export LESS_TERMCAP_mb=$'\e[1;32m' -export LESS_TERMCAP_md=$'\e[1;32m' -export LESS_TERMCAP_me=$'\e[0m' -export LESS_TERMCAP_se=$'\e[0m' -export LESS_TERMCAP_so=$'\e[01;33m' -export LESS_TERMCAP_ue=$'\e[0m' -export LESS_TERMCAP_us=$'\e[1;4;31m' +: "${LESS_TERMCAP_mb:=$'\e[1;32m'}" +: "${LESS_TERMCAP_md:=$'\e[1;32m'}" +: "${LESS_TERMCAP_me:=$'\e[0m'}" +: "${LESS_TERMCAP_se:=$'\e[0m'}" +: "${LESS_TERMCAP_so:=$'\e[01;33m'}" +: "${LESS_TERMCAP_ue:=$'\e[0m'}" +: "${LESS_TERMCAP_us:=$'\e[1;4;31m'}" + +: "${LESS:=}" +export "${!LESS_TERMCAP@}" +export LESS="R${LESS#-}" diff --git a/plugins/available/nginx.plugin.bash b/plugins/available/nginx.plugin.bash index c540a23a..8d75a881 100644 --- a/plugins/available/nginx.plugin.bash +++ b/plugins/available/nginx.plugin.bash @@ -1,66 +1,55 @@ -cite about-plugin +# shellcheck shell=bash about-plugin 'manage your nginx service' -export NGINX_PATH='/opt/nginx' -pathmunge $NGINX_PATH/sbin after +pathmunge "${NGINX_PATH:=/opt/nginx}/sbin" after +export NGINX_PATH function nginx_reload() { - about 'reload your nginx config' - group 'nginx' + about 'reload your nginx config' + group 'nginx' - FILE="${NGINX_PATH}/logs/nginx.pid" - if [ -e $FILE ]; then - echo "Reloading NGINX..." - PID=`cat $NGINX_PATH/logs/nginx.pid` - sudo kill -HUP $PID - else - echo "Nginx pid file not found" - return 0 - fi + local FILE="${NGINX_PATH?}/logs/nginx.pid" + if [[ -s $FILE ]]; then + echo "Reloading NGINX..." + read -r PID < "${FILE}" + sudo kill -HUP "${PID?}" + else + echo "Nginx pid file not found" + return 0 + fi } function nginx_stop() { - about 'stop nginx' - group 'nginx' + about 'stop nginx' + group 'nginx' - FILE="${NGINX_PATH}/logs/nginx.pid" - if [ -e $FILE ]; then - echo "Stopping NGINX..." - PID=`cat $NGINX_PATH/logs/nginx.pid` - sudo kill -INT $PID - else - echo "Nginx pid file not found" - return 0 - fi + local FILE="${NGINX_PATH?}/logs/nginx.pid" + if [[ -s $FILE ]]; then + echo "Stopping NGINX..." + read -r PID < "${FILE}" + sudo kill -INT "${PID?}" + else + echo "Nginx pid file not found" + return 0 + fi } function nginx_start() { - about 'start nginx' - group 'nginx' + about 'start nginx' + group 'nginx' - FILE="${NGINX_PATH}/sbin/nginx" - if [ -e $FILE ]; then - echo "Starting NGINX..." - sudo $NGINX_PATH/sbin/nginx - else - echo "Couldn't start nginx" - fi + local FILE="${NGINX_PATH?}/sbin/nginx" + if [[ -x $FILE ]]; then + echo "Starting NGINX..." + sudo "${FILE}" + else + echo "Couldn't start nginx" + fi } function nginx_restart() { - about 'restart nginx' - group 'nginx' + about 'restart nginx' + group 'nginx' - FILE="${NGINX_PATH}/logs/nginx.pid" - if [ -e $FILE ]; then - echo "Stopping NGINX..." - PID=`cat $NGINX_PATH/logs/nginx.pid` - sudo kill -INT $PID - sleep 1 - echo "Starting NGINX..." - sudo $NGINX_PATH/sbin/nginx - else - echo "Nginx pid file not found" - return 0 - fi + nginx_stop && nginx_start } diff --git a/plugins/available/node.plugin.bash b/plugins/available/node.plugin.bash index 65df3da3..8bf876df 100644 --- a/plugins/available/node.plugin.bash +++ b/plugins/available/node.plugin.bash @@ -1,13 +1,14 @@ +# shellcheck shell=bash cite about-plugin about-plugin 'Node.js helper functions' +# Check that we have npm +_command_exists npm || return + # Ensure local modules are preferred in PATH pathmunge "./node_modules/.bin" "after" -# Check that we have npm -out=$(command -v npm 2>&1) || return - # If not using nodenv, ensure global modules are in PATH -if [[ ! $out == *"nodenv/shims"* ]] ; then - pathmunge "$(npm config get prefix)/bin" "after" +if [[ ! "$(type -p npm)" == *"nodenv/shims"* ]]; then + pathmunge "$(npm config get prefix)/bin" "after" fi diff --git a/plugins/available/nodenv.plugin.bash b/plugins/available/nodenv.plugin.bash index 1bbe7fbd..262a57c3 100644 --- a/plugins/available/nodenv.plugin.bash +++ b/plugins/available/nodenv.plugin.bash @@ -1,7 +1,10 @@ +# shellcheck shell=bash cite about-plugin about-plugin 'load nodenv, if you are using it' export NODENV_ROOT="$HOME/.nodenv" pathmunge "$NODENV_ROOT/bin" -[[ `which nodenv` ]] && eval "$(nodenv init - bash)" +if _command_exists nodenv; then + eval "$(nodenv init - bash)" +fi diff --git a/plugins/available/nvm.plugin.bash b/plugins/available/nvm.plugin.bash index 87dce644..0e6da5d8 100644 --- a/plugins/available/nvm.plugin.bash +++ b/plugins/available/nvm.plugin.bash @@ -1,22 +1,23 @@ -# Bash-it no longer bundles nvm, as this was quickly becoming outdated. +# shellcheck shell=bash # # BASH_IT_LOAD_PRIORITY: 225 # +# Bash-it no longer bundles nvm, as this was quickly becoming outdated. # Please install nvm from https://github.com/creationix/nvm.git if you want to use it. cite about-plugin about-plugin 'node version manager configuration' -export NVM_DIR=${NVM_DIR:-$HOME/.nvm} +export NVM_DIR="${NVM_DIR:-$HOME/.nvm}" # This loads nvm -if _bash_it_homebrew_check && [ -s "${BASH_IT_HOMEBREW_PREFIX}/nvm.sh" ] +if _bash_it_homebrew_check && [[ -s "${BASH_IT_HOMEBREW_PREFIX}/nvm.sh" ]] then - . "${BASH_IT_HOMEBREW_PREFIX}/nvm.sh" + source "${BASH_IT_HOMEBREW_PREFIX}/nvm.sh" else - [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" + [[ -s "$NVM_DIR/nvm.sh" ]] && source "$NVM_DIR/nvm.sh" fi -if ! command -v nvm &>/dev/null +if ! _command_exists nvm then function nvm() { echo "Bash-it no longer bundles the nvm script. Please install the latest version from" diff --git a/plugins/available/osx-timemachine.plugin.bash b/plugins/available/osx-timemachine.plugin.bash index f29d6ece..e30d37bd 100644 --- a/plugins/available/osx-timemachine.plugin.bash +++ b/plugins/available/osx-timemachine.plugin.bash @@ -1,84 +1,95 @@ -cite about-plugin +# shellcheck shell=bash about-plugin 'OS X Time Machine functions' -function time-machine-destination() { - group "osx-timemachine" - about "Shows the OS X Time Machine destination/mount point" +if [[ "${OSTYPE}" != 'darwin'* ]]; then + _log_warning "This plugin only works with Mac OS X" + return 1 +fi - echo $(tmutil destinationinfo | grep "Mount Point" | sed -e 's/Mount Point : \(.*\)/\1/g') +function time-machine-destination() { + group "osx-timemachine" + about "Shows the OS X Time Machine destination/mount point" + + tmutil destinationinfo | grep "Mount Point" | sed -e 's/Mount Point : \(.*\)/\1/g' } function time-machine-list-machines() { - group "osx-timemachine" - about "Lists the OS X Time Machine machines on the backup volume" + group "osx-timemachine" + about "Lists the OS X Time Machine machines on the backup volume" - local tmdest="$(time-machine-destination)/Backups.backupdb" + local tmdest + tmdest="$(time-machine-destination)/Backups.backupdb" - find "$tmdest" -maxdepth 1 -mindepth 1 -type d | grep -v "/\." | while read line ; do - echo "$(basename "$line")" - done + find "$tmdest" -maxdepth 1 -mindepth 1 -type d | grep -v "/\." | while read -r line; do + echo "${line##*/}" + done } function time-machine-list-all-backups() { - group "osx-timemachine" - about "Shows all of the backups for the specified machine" - param "1: Machine name (optional)" - example "time-machine-list-all-backups my-laptop" + group "osx-timemachine" + about "Shows all of the backups for the specified machine" + param "1: Machine name (optional)" + example "time-machine-list-all-backups my-laptop" - # Use the local hostname if none provided - local COMPUTERNAME=${1:-$(scutil --get ComputerName)} - local BACKUP_LOCATION="$(time-machine-destination)/Backups.backupdb/$COMPUTERNAME" + # Use the local hostname if none provided + local COMPUTERNAME BACKUP_LOCATION + COMPUTERNAME=${1:-$(scutil --get ComputerName)} + BACKUP_LOCATION="$(time-machine-destination)/Backups.backupdb/$COMPUTERNAME" - find "$BACKUP_LOCATION" -maxdepth 1 -mindepth 1 -type d | while read line ; do - echo "$line" - done + find "$BACKUP_LOCATION" -maxdepth 1 -mindepth 1 -type d | while read -r line; do + echo "$line" + done } function time-machine-list-old-backups() { - group "osx-timemachine" - about "Shows all of the backups for the specified machine, except for the most recent backup" - param "1: Machine name (optional)" - example "time-machine-list-old-backups my-laptop" + group "osx-timemachine" + about "Shows all of the backups for the specified machine, except for the most recent backup" + param "1: Machine name (optional)" + example "time-machine-list-old-backups my-laptop" - # Use the local hostname if none provided - local COMPUTERNAME=${1:-$(scutil --get ComputerName)} - local BACKUP_LOCATION="$(time-machine-destination)/Backups.backupdb/$COMPUTERNAME" + # Use the local hostname if none provided + local COMPUTERNAME BACKUP_LOCATION + COMPUTERNAME=${1:-$(scutil --get ComputerName)} + BACKUP_LOCATION="$(time-machine-destination)/Backups.backupdb/$COMPUTERNAME" - # List all but the most recent one - find "$BACKUP_LOCATION" -maxdepth 1 -mindepth 1 -type d -name 2\* | sed \$d | while read line ; do - echo "$line" - done + # List all but the most recent one + find "$BACKUP_LOCATION" -maxdepth 1 -mindepth 1 -type d -name 2\* | sed \$d | while read -r line; do + echo "$line" + done } # Taken from here: http://stackoverflow.com/a/30547074/1228454 function _tm_startsudo() { - sudo -v - ( while true; do sudo -v; sleep 50; done; ) & - SUDO_PID="$!" - trap _tm_stopsudo SIGINT SIGTERM + local -x SUDO_COMMAND="plugin/osx-timemachine: keep 'sudo' token alive during long-run 'tmutil' commands" + sudo "-${SUDO_ASKPASS:+A}v" # validate without running a command, using `ssh-askpass` if available. + (while sudo "-${SUDO_ASKPASS:+A}v"; do + sleep 50 + done) & + SUDO_PID="$!" + trap _tm_stopsudo SIGINT SIGTERM } function _tm_stopsudo() { - kill "$SUDO_PID" - trap - SIGINT SIGTERM - sudo -k + kill "$SUDO_PID" + trap - SIGINT SIGTERM + sudo -k } function time-machine-delete-old-backups() { - group "osx-timemachine" - about "Deletes all of the backups for the specified machine, with the exception of the most recent one" - param "1: Machine name (optional)" - example "time-machine-delete-old-backups my-laptop" + group "osx-timemachine" + about "Deletes all of the backups for the specified machine, with the exception of the most recent one" + param "1: Machine name (optional)" + example "time-machine-delete-old-backups my-laptop" - # Use the local hostname if none provided - local COMPUTERNAME=${1:-$(scutil --get ComputerName)} + # Use the local hostname if none provided + local COMPUTERNAME=${1:-$(scutil --get ComputerName)} _old_backup - # Ask for sudo credentials only once - _tm_startsudo + # Ask for sudo credentials only once + _tm_startsudo - echo "$(time-machine-list-old-backups "$COMPUTERNAME")" | while read i ; do - # Delete the backup - sudo tmutil delete "$i" - done + while read -r _old_backup; do + # Delete the backup + sudo tmutil delete "$_old_backup" + done <<< "$(time-machine-list-old-backups "$COMPUTERNAME")" - _tm_stopsudo + _tm_stopsudo } diff --git a/plugins/available/osx.plugin.bash b/plugins/available/osx.plugin.bash index ca1f66b8..139f58a1 100644 --- a/plugins/available/osx.plugin.bash +++ b/plugins/available/osx.plugin.bash @@ -1,23 +1,21 @@ -cite about-plugin +# shellcheck shell=bash about-plugin 'osx-specific functions' +if [[ "${OSTYPE}" != 'darwin'* ]]; then + _log_warning "This plugin only works with Mac OS X." + return 1 +fi + # OS X: Open new tabs in same directory -if [[ $OSTYPE == 'darwin'* ]]; then - if type update_terminal_cwd > /dev/null 2>&1 ; then - if ! [[ $PROMPT_COMMAND =~ (^|;)update_terminal_cwd($|;) ]] ; then - PROMPT_COMMAND="${PROMPT_COMMAND%;};update_terminal_cwd" - declared="$(declare -p PROMPT_COMMAND)" - [[ "$declared" =~ \ -[aAilrtu]*x[aAilrtu]*\ ]] 2>/dev/null - [[ $? -eq 0 ]] && export PROMPT_COMMAND - fi - fi +if _is_function update_terminal_cwd; then + safe_append_prompt_command 'update_terminal_cwd' fi function tab() { - about 'opens a new terminal tab' - group 'osx' + about 'opens a new terminal tab' + group 'osx' - osascript 2>/dev/null < /dev/null << EOF tell application "System Events" tell process "Terminal" to keystroke "t" using command down end @@ -30,147 +28,105 @@ EOF # renames the current os x terminal tab title function tabname { - printf "\e]1;$1\a" + printf '%b' "\e]1;$1\a" } # renames the current os x terminal window title function winname { - printf "\e]2;$1\a" + printf '%b' "\e]2;$1\a" } -# this one switches your os x dock between 2d and 3d -# thanks to savier.zwetschge.org -function dock-switch() { - about 'switch dock between 2d and 3d' - param '1: "2d" or "3d"' - example '$ dock-switch 2d' - group 'osx' - - if [[ "$OSTYPE" = 'darwin'* ]]; then - - if [[ $1 = 3d ]] ; then - defaults write com.apple.dock no-glass -boolean NO - killall Dock - - elif [[ $1 = 2d ]] ; then - defaults write com.apple.dock no-glass -boolean YES - killall Dock - - else - echo "usage:" - echo "dock-switch 2d" - echo "dock-switch 3d." - fi - else - echo "Sorry, this only works on Mac OS X" - fi +function pman() { + about 'view man documentation in Preview' + param '1: man page to view' + example '$ pman bash' + group 'osx' + man -t "${1}" | open -fa 'Preview' } -function pman () -{ - about 'view man documentation in Preview' - param '1: man page to view' - example '$ pman bash' - group 'osx' - man -t "${1}" | open -fa $PREVIEW -} - -function pri () -{ - about 'display information about Ruby classes, modules, or methods, in Preview' - param '1: Ruby method, module, or class' - example '$ pri Array' - group 'osx' - ri -T "${1}" | open -fa $PREVIEW +function pri() { + about 'display information about Ruby classes, modules, or methods, in Preview' + param '1: Ruby method, module, or class' + example '$ pri Array' + group 'osx' + ri -T "${1}" | open -fa 'Preview' } # Download a file and open it in Preview function prevcurl() { - about 'download a file and open it in Preview' - param '1: url' - group 'osx' + about 'download a file and open it in Preview' + param '1: url' + group 'osx' - if [[ ! $OSTYPE = 'darwin'* ]] - then - echo "This function only works with Mac OS X" - return 1 - fi - curl "$*" | open -fa $PREVIEW + curl "$*" | open -fa 'Preview' } function refresh-launchpad() { - about 'Reset launchpad layout in macOS' - example '$ refresh-launchpad' - group 'osx' + about 'Reset launchpad layout in macOS' + example '$ refresh-launchpad' + group 'osx' - if [[ "$OSTYPE" = 'darwin'* ]];then - defaults write com.apple.dock ResetLaunchPad -bool TRUE - killall Dock - else - echo "Sorry, this only works on Mac OS X" - fi + defaults write com.apple.dock ResetLaunchPad -bool TRUE + killall Dock } -function list-jvms(){ - about 'List java virtual machines and their states in macOS' - example 'list-jvms' - group 'osx' +function list-jvms() { + about 'List java virtual machines and their states in macOS' + example 'list-jvms' + group 'osx' - JVMS_DIR="/Library/Java/JavaVirtualMachines" - JVMS=( $(ls ${JVMS_DIR}) ) - JVMS_STATES=() + local JVMS_DIR="/Library/Java/JavaVirtualMachines" + # The following variables are intended to impact the enclosing scope, not local. + JVMS=("${JVMS_DIR}"/*) + JVMS_STATES=() - # Map state of JVM - for (( i = 0; i < ${#JVMS[@]}; i++ )); do - if [[ -f "${JVMS_DIR}/${JVMS[$i]}/Contents/Info.plist" ]]; then - JVMS_STATES[${i}]=enabled - else - JVMS_STATES[${i}]=disabled - fi - echo "${i} ${JVMS[$i]} ${JVMS_STATES[$i]}" - done + # Map state of JVM + for ((i = 0; i < ${#JVMS[@]}; i++)); do + if [[ -f "${JVMS[i]}/Contents/Info.plist" ]]; then + JVMS_STATES[i]=enabled + else + JVMS_STATES[i]=disabled + fi + printf '%s\t%s\t%s\n' "${i}" "${JVMS[i]##*/}" "${JVMS_STATES[i]}" + done } -function pick-default-jvm(){ - about 'Pick the default Java Virtual Machines in system-wide scope in macOS' - example 'pick-default-jvm' +function pick-default-jvm() { + about 'Pick the default Java Virtual Machines in system-wide scope in macOS' + example 'pick-default-jvm' - # Call function for listing - list-jvms + # Declare variables + local JVMS JVMS_STATES + local DEFAULT_JVM_DIR DEFAULT_JVM OPTION - # Declare variables - local DEFAULT_JVM_DIR="" - local DEFAULT_JVM="" - local OPTION="" + # Call function for listing + list-jvms - # OPTION for default jdk and set variables - while [[ ! "$OPTION" =~ ^[0-9]+$ || OPTION -ge "${#JVMS[@]}" ]]; do - read -p "Enter Default JVM: " OPTION - if [[ ! "$OPTION" =~ ^[0-9]+$ ]]; then - echo "Please enter a number" - fi + # OPTION for default jdk and set variables + while [[ ! "$OPTION" =~ ^[0-9]+$ || OPTION -ge "${#JVMS[@]}" ]]; do + read -rp "Enter Default JVM: " OPTION + if [[ ! "$OPTION" =~ ^[0-9]+$ ]]; then + echo "Please enter a number" + fi - if [[ OPTION -ge "${#JVMS[@]}" ]]; then - echo "Please select one of the displayed JVMs" - fi - done + if [[ OPTION -ge "${#JVMS[@]}" ]]; then + echo "Please select one of the displayed JVMs" + fi + done - DEFAULT_JVM_DIR="${JVMS_DIR}/${JVMS[$OPTION]}" - DEFAULT_JVM="${JVMS[$OPTION]}" + DEFAULT_JVM_DIR="${JVMS[OPTION]}" + DEFAULT_JVM="${JVMS[OPTION]##*/}" - # Disable all jdk - for (( i = 0; i < ${#JVMS[@]}; i++ )); do - if [[ -f "${JVMS_DIR}/${JVMS[$i]}/Contents/Info.plist" ]]; then - sudo mv "${JVMS_DIR}/${JVMS[$i]}/Contents/Info.plist" "${JVMS_DIR}/${JVMS[$i]}/Contents/Info.plist.disable" - fi - done + # Disable all jdk + for ((i = 0; i < ${#JVMS[@]}; i++)); do + if [[ "${JVMS[i]}" != "${DEFAULT_JVM_DIR}" && -f "${JVMS[i]}/Contents/Info.plist" ]]; then + sudo mv "${JVMS[i]}/Contents/Info.plist" "${JVMS[i]}/Contents/Info.plist.disable" + fi + done - # Enable default jdk - if [[ -f "${DEFAULT_JVM_DIR}/Contents/Info.plist.disable" ]]; then - sudo mv "${DEFAULT_JVM_DIR}/Contents/Info.plist.disable" "${DEFAULT_JVM_DIR}/Contents/Info.plist" - echo "Enabled ${DEFAULT_JVM} as default JVM" - fi + # Enable default jdk + if [[ -f "${DEFAULT_JVM_DIR}/Contents/Info.plist.disable" ]]; then + sudo mv -vn "${DEFAULT_JVM_DIR}/Contents/Info.plist.disable" "${DEFAULT_JVM_DIR}/Contents/Info.plist" \ + && echo "Enabled ${DEFAULT_JVM} as default JVM" + fi } - -# Make this backwards compatible -alias pcurl='prevcurl' diff --git a/plugins/available/percol.plugin.bash b/plugins/available/percol.plugin.bash index c1fc807c..027dfdc4 100644 --- a/plugins/available/percol.plugin.bash +++ b/plugins/available/percol.plugin.bash @@ -1,3 +1,4 @@ +# shellcheck shell=bash cite about-plugin about-plugin 'Search&Select history with percol' @@ -12,24 +13,20 @@ about-plugin 'Search&Select history with percol' # Usage ## C-r to search&select from history -_replace_by_history() { - if command -v tac>/dev/null; then - alias _tac=tac - else - alias _tac="tail -r" - fi - local l=$(HISTTIMEFORMAT= history | _tac | sed -e 's/^\ *[0-9]*\ *//' | percol --query "$READLINE_LINE") - READLINE_LINE="$l" - READLINE_POINT=${#l} -} +_command_exists percol || return - -if command -v percol>/dev/null; then - current_version=${BASH_VERSION%%[^0-9]*} - if [ $current_version -lt 4 ]; then - echo -e "\033[91mWarning: You have to upgrade Bash to Bash v4.x to use the 'percol' plugin.\033[m" - echo -e "\033[91m Your current Bash version is $BASH_VERSION.\033[m" - else - bind -x '"\C-r": _replace_by_history' - fi +if [[ ${BASH_VERSINFO[0]} -lt 4 ]]; then + _log_warning "You have to upgrade Bash to Bash v4.x to use the 'percol' plugin." + _log_warning "Your current Bash version is $BASH_VERSION." + return fi + +function _replace_by_history() { + local HISTTIMEFORMAT= # Ensure we can parse history properly + #TODO: "${histlines[@]/*( )+([[:digit:]])*( )/}" + local l + l="$(history | tail -r | sed -e 's/^\ *[0-9]*\ *//' | percol --query "${READLINE_LINE:-}")" + READLINE_LINE="${l}" + READLINE_POINT=${#l} +} +bind -x '"\C-r": _replace_by_history' diff --git a/plugins/available/plenv.plugin.bash b/plugins/available/plenv.plugin.bash index 1da2d61b..79a9cf49 100644 --- a/plugins/available/plenv.plugin.bash +++ b/plugins/available/plenv.plugin.bash @@ -1,18 +1,16 @@ +# shellcheck shell=bash +# # plugin for plenv cite about-plugin about-plugin 'plenv plugin for Perl' -if [[ -e "${HOME}/.plenv/bin" ]] ; then - - # load plenv bin dir into path if it exists - pathmunge "${HOME}/.plenv/bin" - +if [[ -d "${HOME}/.plenv/bin" ]]; then + # load plenv bin dir into path if it exists + pathmunge "${HOME}/.plenv/bin" fi -if [[ `which plenv` ]] ; then - - # init plenv - eval "$(plenv init - bash)" - +if _command_exists plenv; then + # init plenv + eval "$(plenv init - bash)" fi diff --git a/plugins/available/powerline.plugin.bash b/plugins/available/powerline.plugin.bash index 3d91e658..1927bf3e 100644 --- a/plugins/available/powerline.plugin.bash +++ b/plugins/available/powerline.plugin.bash @@ -1,9 +1,9 @@ -#!/usr/bin/env bash +# shellcheck shell=bash cite about-plugin about-plugin 'enables powerline daemon' -command -v powerline-daemon &>/dev/null || return +_command_exists powerline-daemon || return powerline-daemon -q #the following should not be executed if bashit powerline themes in use @@ -14,13 +14,13 @@ case "$BASH_IT_THEME" in esac POWERLINE_BASH_CONTINUATION=1 POWERLINE_BASH_SELECT=1 -bashPowerlineInit=$(python -c \ +bashPowerlineInit="$(python -c \ "import os; \ import powerline;\ print(os.path.join(os.path.dirname(\ powerline.__file__),\ 'bindings', \ 'bash', \ - 'powerline.sh'))") + 'powerline.sh'))")" [ -e $bashPowerlineInit ] || return -. $bashPowerlineInit +source $bashPowerlineInit diff --git a/plugins/available/projects.plugin.bash b/plugins/available/projects.plugin.bash index 775ec813..34fa001e 100644 --- a/plugins/available/projects.plugin.bash +++ b/plugins/available/projects.plugin.bash @@ -1,75 +1,57 @@ -cite about-plugin -about-plugin 'quickly navigate configured paths with `pj` and `pjo`. example: "export PROJECT_PATHS=~/projects:~/work/projects"' +# shellcheck shell=bash +about-plugin 'quickly navigate configured project paths' -function pj { -about 'navigate quickly to your various project directories' -group 'projects' +: "${BASH_IT_PROJECT_PATHS:=$HOME/Projects:$HOME/src:$HOME/work}" +function pj() { + about 'navigate quickly to your various project directories' + group 'projects' -if [ -z "$PROJECT_PATHS" ]; then - echo "error: PROJECT_PATHS not set" - return 1 -fi + local proj="${1?${FUNCNAME[0]}: project name required}" + local cmd PS3 dest d + local -a dests + if [[ "$proj" == "open" ]]; then + shift + proj="${1}" + cmd="${EDITOR?}" + fi -local cmd -local dest -local -a dests + # collect possible destinations to account for directories + # with the same name in project directories + IFS=':' read -ra dests <<< "${BASH_IT_PROJECT_PATHS?${FUNCNAME[0]}: project working folders must be configured}" + for d in "${!dests[@]}"; do + if [[ ! -d "${dests[d]}/${proj}" ]]; then + unset 'dests[d]' + fi + done + case ${#dests[@]} in + 0) + _log_error "BASH_IT_PROJECT_PATHS must contain at least one existing location" + return 1 + ;; + 1) + dest="${dests[*]}/${proj}" + ;; + *) + PS3="Multiple project directories found. Please select one: " + dests+=("cancel") + select d in "${dests[@]}"; do + case $d in + "cancel") + return + ;; + *) + dest="${d}/${proj}" + break + ;; + esac + done + ;; + esac -if [ "$1" == "open" ]; then - shift - cmd="$EDITOR" -fi -cmd="${cmd:-cd}" - - -if [ -z "$1" ]; then - echo "error: no project provided" - return 1 -fi - - -# collect possible destinations to account for directories -# with the same name in project directories -for i in ${PROJECT_PATHS//:/$'\n'}; do - if [ -d "$i"/"$1" ]; then - dests+=("$i/$1") - fi -done - - -# when multiple destinations are found, present a menu -if [ ${#dests[@]} -eq 0 ]; then - echo "error: no such project '$1'" - return 1 - -elif [ ${#dests[@]} -eq 1 ]; then - dest="${dests[0]}" - -elif [ ${#dests[@]} -gt 1 ]; then - PS3="Multiple project directories found. Please select one: " - dests+=("cancel") - select d in "${dests[@]}"; do - case $d in - "cancel") - return - ;; - *) - dest=$d - break - ;; - esac - done - -else - echo "error: please report this error" - return 1 # should never reach this - -fi - - -$cmd "$dest" + "${cmd:-cd}" "${dest}" } alias pjo="pj open" diff --git a/plugins/available/proxy.plugin.bash b/plugins/available/proxy.plugin.bash index f2458e3f..985d77b4 100644 --- a/plugins/available/proxy.plugin.bash +++ b/plugins/available/proxy.plugin.bash @@ -1,8 +1,7 @@ -cite about-plugin +# shellcheck shell=bash about-plugin 'Proxy Tools' -disable-proxy () -{ +function disable-proxy() { about 'Disables proxy settings for Bash, npm and SSH' group 'proxy' @@ -20,18 +19,17 @@ disable-proxy () svn-disable-proxy } -enable-proxy () -{ +function enable-proxy() { about 'Enables proxy settings for Bash, npm and SSH' group 'proxy' - export http_proxy=$BASH_IT_HTTP_PROXY - export https_proxy=$BASH_IT_HTTPS_PROXY - export HTTP_PROXY=$http_proxy - export HTTPS_PROXY=$https_proxy - export ALL_PROXY=$http_proxy - export no_proxy=$BASH_IT_NO_PROXY - export NO_PROXY=$no_proxy + export http_proxy="${BASH_IT_HTTP_PROXY:-}" + export https_proxy="${BASH_IT_HTTPS_PROXY:-}" + export HTTP_PROXY="${http_proxy:-}" + export HTTPS_PROXY="${https_proxy:-}" + export ALL_PROXY="${http_proxy:-}" + export no_proxy="${BASH_IT_NO_PROXY:-}" + export NO_PROXY="${no_proxy:-}" echo "Enabled proxy environment variables" npm-enable-proxy @@ -39,27 +37,25 @@ enable-proxy () svn-enable-proxy } -enable-proxy-alt () -{ +function enable-proxy-alt() { about 'Enables alternate proxy settings for Bash, npm and SSH' group 'proxy' - export http_proxy=$BASH_IT_HTTP_PROXY_ALT - export https_proxy=$BASH_IT_HTTPS_PROXY_ALT - export HTTP_PROXY=$http_proxy - export HTTPS_PROXY=$https_proxy - export ALL_PROXY=$http_proxy - export no_proxy=$BASH_IT_NO_PROXY - export NO_PROXY=$no_proxy + export http_proxy="${BASH_IT_HTTP_PROXY_ALT:-}" + export https_proxy="${BASH_IT_HTTPS_PROXY_ALT:-}" + export HTTP_PROXY="${http_proxy:-}" + export HTTPS_PROXY="${https_proxy:-}" + export ALL_PROXY="${http_proxy:-}" + export no_proxy="${BASH_IT_NO_PROXY:-}" + export NO_PROXY="${no_proxy:-}" echo "Enabled alternate proxy environment variables" - npm-enable-proxy $http_proxy $https_proxy + npm-enable-proxy "${http_proxy:-}" "${https_proxy:-}" ssh-enable-proxy - svn-enable-proxy $http_proxy + svn-enable-proxy "${http_proxy:-}" } -show-proxy () -{ +function show-proxy() { about 'Shows the proxy settings for Bash, Git, npm and SSH' group 'proxy' @@ -75,8 +71,7 @@ show-proxy () ssh-show-proxy } -proxy-help () -{ +function proxy-help() { about 'Provides an overview of the bash-it proxy configuration' group 'proxy' @@ -97,8 +92,7 @@ EOF bash-it-show-proxy } -bash-it-show-proxy () -{ +function bash-it-show-proxy() { about 'Shows the bash-it proxy settings' group 'proxy' @@ -110,27 +104,25 @@ bash-it-show-proxy () env | grep -e "BASH_IT.*PROXY" } -npm-show-proxy () -{ +function npm-show-proxy() { about 'Shows the npm proxy settings' group 'proxy' - if $(command -v npm &> /dev/null) ; then + if _command_exists npm; then echo "" echo "npm" echo "===" - echo "npm HTTP proxy: " `npm config get proxy` - echo "npm HTTPS proxy: " `npm config get https-proxy` - echo "npm proxy exceptions: " `npm config get noproxy` + echo "npm HTTP proxy: $(npm config get proxy)" + echo "npm HTTPS proxy: $(npm config get https-proxy)" + echo "npm proxy exceptions: $(npm config get noproxy)" fi } -npm-disable-proxy () -{ +function npm-disable-proxy() { about 'Disables npm proxy settings' group 'proxy' - if $(command -v npm &> /dev/null) ; then + if _command_exists npm; then npm config delete proxy npm config delete https-proxy npm config delete noproxy @@ -138,113 +130,104 @@ npm-disable-proxy () fi } -npm-enable-proxy () -{ +function npm-enable-proxy() { about 'Enables npm proxy settings' group 'proxy' - local my_http_proxy=${1:-$BASH_IT_HTTP_PROXY} - local my_https_proxy=${2:-$BASH_IT_HTTPS_PROXY} - local my_no_proxy=${3:-$BASH_IT_NO_PROXY} + local my_http_proxy="${1:-${BASH_IT_HTTP_PROXY:-}}" + local my_https_proxy="${2:-${BASH_IT_HTTPS_PROXY:-}}" + local my_no_proxy="${3:-${BASH_IT_NO_PROXY:-}}" - if $(command -v npm &> /dev/null) ; then - npm config set proxy $my_http_proxy - npm config set https-proxy $my_https_proxy - npm config set noproxy $my_no_proxy + if _command_exists npm; then + npm config set proxy "${my_http_proxy:?}" || return + npm config set https-proxy "${my_https_proxy:?}" || return + npm config set noproxy "${my_no_proxy:-}" || return echo "Enabled npm proxy settings" fi } -git-global-show-proxy () -{ +function git-global-show-proxy() { about 'Shows global Git proxy settings' group 'proxy' - if $(command -v git &> /dev/null) ; then + if _command_exists git; then echo "" echo "Git (Global Settings)" echo "=====================" - echo "Git (Global) HTTP proxy: " `git config --global --get http.proxy` - echo "Git (Global) HTTPS proxy: " `git config --global --get https.proxy` + echo "Git (Global) HTTP proxy: $(git config --global --get http.proxy)" + echo "Git (Global) HTTPS proxy: $(git config --global --get https.proxy)" fi } -git-global-disable-proxy () -{ +function git-global-disable-proxy() { about 'Disables global Git proxy settings' group 'proxy' - if $(command -v git &> /dev/null) ; then + if _command_exists git; then git config --global --unset-all http.proxy git config --global --unset-all https.proxy echo "Disabled global Git proxy settings" fi } -git-global-enable-proxy () -{ +function git-global-enable-proxy() { about 'Enables global Git proxy settings' group 'proxy' - if $(command -v git &> /dev/null) ; then + if _command_exists git; then git-global-disable-proxy - git config --global --add http.proxy $BASH_IT_HTTP_PROXY - git config --global --add https.proxy $BASH_IT_HTTPS_PROXY + git config --global --add http.proxy "${BASH_IT_HTTP_PROXY:?}" + git config --global --add https.proxy "${BASH_IT_HTTPS_PROXY:?}" echo "Enabled global Git proxy settings" fi } -git-show-proxy () -{ +function git-show-proxy() { about 'Shows current Git project proxy settings' group 'proxy' - if $(command -v git &> /dev/null) ; then + if _command_exists git; then echo "Git Project Proxy Settings" echo "=====================" - echo "Git HTTP proxy: " `git config --get http.proxy` - echo "Git HTTPS proxy: " `git config --get https.proxy` + echo "Git HTTP proxy: $(git config --get http.proxy)" + echo "Git HTTPS proxy: $(git config --get https.proxy)" fi } -git-disable-proxy () -{ +function git-disable-proxy() { about 'Disables current Git project proxy settings' group 'proxy' - if $(command -v git &> /dev/null) ; then + if _command_exists git; then git config --unset-all http.proxy git config --unset-all https.proxy echo "Disabled Git project proxy settings" fi } -git-enable-proxy () -{ +function git-enable-proxy() { about 'Enables current Git project proxy settings' group 'proxy' - if $(command -v git &> /dev/null) ; then + if _command_exists git; then git-disable-proxy - git config --add http.proxy $BASH_IT_HTTP_PROXY - git config --add https.proxy $BASH_IT_HTTPS_PROXY + git config --add http.proxy "${BASH_IT_HTTP_PROXY:?}" + git config --add https.proxy "${BASH_IT_HTTPS_PROXY:?}" echo "Enabled Git project proxy settings" fi } - -svn-show-proxy () -{ +function svn-show-proxy() { about 'Shows SVN proxy settings' group 'proxy' - if $(command -v svn &> /dev/null) && $(command -v python2 &> /dev/null) ; then + if _command_exists svn && _command_exists python2; then echo "" echo "SVN Proxy Settings" echo "==================" - python2 - < /dev/null) && $(command -v python2 &> /dev/null) ; then - python2 - < /dev/null) && $(command -v python2 &> /dev/null) ; then - local my_http_proxy=${1:-$BASH_IT_HTTP_PROXY} + if _command_exists svn _command_exists python2; then + local my_http_proxy="${1:-${BASH_IT_HTTP_PROXY:-}}" - python2 - "$my_http_proxy" "$BASH_IT_NO_PROXY" </dev/null` ]] && eval "$(pyenv init - bash)" +# Load after basher +# BASH_IT_LOAD_PRIORITY: 260 -#Load pyenv virtualenv if the virtualenv plugin is installed. -if pyenv virtualenv-init - &> /dev/null; then - eval "$(pyenv virtualenv-init - bash)" +# Don't modify the environment if we can't find the tool: +# - Check if in $PATH already +# - Check if installed manually to $PYENV_ROOT +# - Check if installed manually to $HOME +_command_exists pyenv \ + || [[ -n "$PYENV_ROOT" && -x "$PYENV_ROOT/bin/pyenv" ]] \ + || [[ -x "$HOME/.pyenv/bin/pyenv" ]] \ + || return 0 + +# Set PYENV_ROOT, if not already set +export PYENV_ROOT="${PYENV_ROOT:-$HOME/.pyenv}" + +# Add PYENV_ROOT/bin to PATH, if that's where it's installed +if ! _command_exists pyenv && [[ -x "$PYENV_ROOT/bin/pyenv" ]]; then + pathmunge "$PYENV_ROOT/bin" +fi + +# Initialize pyenv +pathmunge "$PYENV_ROOT/shims" +eval "$(pyenv init - bash)" + +# Load pyenv virtualenv if the virtualenv plugin is installed. +if pyenv virtualenv-init - &> /dev/null; then + eval "$(pyenv virtualenv-init - bash)" fi diff --git a/plugins/available/python.plugin.bash b/plugins/available/python.plugin.bash index 77fa7dd6..bd644e8b 100644 --- a/plugins/available/python.plugin.bash +++ b/plugins/available/python.plugin.bash @@ -1,31 +1,32 @@ -cite about-plugin +# shellcheck shell=bash about-plugin 'alias "shttp" to SimpleHTTPServer' -if [[ "$OSTYPE" == 'linux'* ]] -then - alias shttp='python2 -m SimpleHTTPServer' +if _command_exists python3; then + alias shttp='python3 -m http.server' +elif _command_exists python; then + alias shttp='python -m http.server' else - alias shttp='python -m SimpleHTTPServer' + _log_warning "Unable to load 'plugin/python' due to being unable to find a working 'python'" + return 1 fi function pyedit() { - about 'opens python module in your EDITOR' - param '1: python module to open' - example '$ pyedit requests' - group 'python' + about 'opens python module in your EDITOR' + param '1: python module to open' + example '$ pyedit requests' + group 'python' - xpyc=`python -c "import os, sys; f = open(os.devnull, 'w'); sys.stderr = f; module = __import__('$1'); sys.stdout.write(module.__file__)"` + xpyc="$(python -c "import os, sys; f = open(os.devnull, 'w'); sys.stderr = f; module = __import__('$1'); sys.stdout.write(module.__file__)")" - if [[ "$xpyc" == "" ]]; then - echo "Python module $1 not found" - return -1 - - elif [[ $xpyc == *__init__.py* ]]; then - xpydir=`dirname $xpyc`; - echo "$EDITOR $xpydir"; - $EDITOR "$xpydir"; - else - echo "$EDITOR ${xpyc%.*}.py"; - $EDITOR "${xpyc%.*}.py"; - fi + if [[ "$xpyc" == "" ]]; then + echo "Python module $1 not found" + return 1 + elif [[ "$xpyc" == *__init__.py* ]]; then + xpydir="${xpyc%/*}" + echo "$EDITOR $xpydir" + ${VISUAL:-${EDITOR:-${ALTERNATE_EDITOR:-nano}}} "$xpydir" + else + echo "$EDITOR ${xpyc%.*}.py" + ${VISUAL:-${EDITOR:-${ALTERNATE_EDITOR:-nano}}} "${xpyc%.*}.py" + fi } diff --git a/plugins/available/rbenv.plugin.bash b/plugins/available/rbenv.plugin.bash index 2b01669d..f3605f58 100644 --- a/plugins/available/rbenv.plugin.bash +++ b/plugins/available/rbenv.plugin.bash @@ -1,7 +1,10 @@ +# shellcheck shell=bash cite about-plugin about-plugin 'load rbenv, if you are using it' export RBENV_ROOT="$HOME/.rbenv" pathmunge "$RBENV_ROOT/bin" -[[ `which rbenv 2>/dev/null` ]] && eval "$(rbenv init - bash)" +if _command_exists rbenv; then + eval "$(rbenv init - bash)" +fi diff --git a/plugins/available/ruby.plugin.bash b/plugins/available/ruby.plugin.bash index 4ab891d9..aed8daf8 100644 --- a/plugins/available/ruby.plugin.bash +++ b/plugins/available/ruby.plugin.bash @@ -1,16 +1,17 @@ +# shellcheck shell=bash cite about-plugin about-plugin 'ruby and rubygems specific functions and settings' # Make commands installed with 'gem install --user-install' available # ~/.gem/ruby/${RUBY_VERSION}/bin/ -if which ruby >/dev/null && which gem >/dev/null; then - pathmunge "$(ruby -e 'print Gem.user_dir')/bin" after +if _command_exists ruby && _command_exists gem; then + pathmunge "$(ruby -e 'print Gem.user_dir')/bin" after fi -function remove_gem { - about 'removes installed gem' - param '1: installed gem name' - group 'ruby' +function remove_gem() { + about 'removes installed gem' + param '1: installed gem name' + group 'ruby' - gem list | grep $1 | awk '{ print $1; }' | xargs sudo gem uninstall + gem list | grep "${1:?${FUNCNAME[0]}: no gem name provided}" | awk '{ print $1; }' | xargs sudo gem uninstall } diff --git a/plugins/available/textmate.plugin.bash b/plugins/available/textmate.plugin.bash index e3538c1e..5c81f195 100644 --- a/plugins/available/textmate.plugin.bash +++ b/plugins/available/textmate.plugin.bash @@ -1,7 +1,9 @@ +# shellcheck shell=bash cite about-plugin about-plugin 'set textmate as a default editor' -if $(command -v mate &> /dev/null) ; then - export EDITOR="$(which mate) -w" - export GIT_EDITOR=$EDITOR +if _command_exists mate; then + EDITOR="$(type -p mate) -w" + GIT_EDITOR="$EDITOR" + export EDITOR GIT_EDITOR fi diff --git a/plugins/available/todo.plugin.bash b/plugins/available/todo.plugin.bash old mode 100755 new mode 100644 index cf1479e2..6b495274 --- a/plugins/available/todo.plugin.bash +++ b/plugins/available/todo.plugin.bash @@ -1,12 +1,6 @@ -#!/bin/bash -cite about-plugin +# shellcheck shell=bash about-plugin 'Todo.txt integration' # you may override any of the exported variables below in your .bash_profile - -if [ -z "$TODOTXT_DEFAULT_ACTION" ]; then - # typing 't' by itself will list current todos - export TODOTXT_DEFAULT_ACTION=ls -fi - -alias t='todo.sh' +: "${TODOTXT_DEFAULT_ACTION:=ls}" +export TODOTXT_DEFAULT_ACTION diff --git a/plugins/available/virtualenv.plugin.bash b/plugins/available/virtualenv.plugin.bash index 468870cd..41d55ddf 100644 --- a/plugins/available/virtualenv.plugin.bash +++ b/plugins/available/virtualenv.plugin.bash @@ -1,12 +1,14 @@ +# shellcheck shell=bash +# # make sure virtualenvwrapper is enabled if available cite about-plugin about-plugin 'virtualenvwrapper and pyenv-virtualenvwrapper helper functions' if _command_exists pyenv; then - pyenv virtualenvwrapper -else - [[ `which virtualenvwrapper.sh` ]] && . virtualenvwrapper.sh + pyenv virtualenvwrapper +elif _command_exists virtualenvwrapper.sh; then + source virtualenvwrapper.sh fi @@ -14,8 +16,8 @@ function mkvenv { about 'create a new virtualenv for this directory' group 'virtualenv' - cwd=`basename \`pwd\`` - mkvirtualenv --distribute $cwd + local cwd="${PWD##*/}" + mkvirtualenv --distribute "$cwd" } @@ -23,19 +25,21 @@ function mkvbranch { about 'create a new virtualenv for the current branch' group 'virtualenv' - mkvirtualenv --distribute "$(basename `pwd`)@$SCM_BRANCH" + local cwd="${PWD##*/}" + mkvirtualenv --distribute "${cwd}@${SCM_BRANCH}" } function wovbranch { about 'sets workon branch' group 'virtualenv' - workon "$(basename `pwd`)@$SCM_BRANCH" + local cwd="${PWD##*/}" + workon "${cwd}@${SCM_BRANCH}" } function wovenv { about 'works on the virtualenv for this directory' group 'virtualenv' - workon "$(basename `pwd`)" + workon "${PWD##*/}" } diff --git a/plugins/available/xterm.plugin.bash b/plugins/available/xterm.plugin.bash index 4f4fd50d..740460e4 100644 --- a/plugins/available/xterm.plugin.bash +++ b/plugins/available/xterm.plugin.bash @@ -3,29 +3,41 @@ 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}" + local dir_name="${PWD/~/\~}" + if [[ "${SHORT_TERM_LINE:-}" == true && "${#dir_name}" -gt 8 ]]; then + echo "${dir_name##*/}" + else + echo "${dir_name}" + fi } _short-command() { local input_command="$*" - [ "$SHORT_TERM_LINE" = true ] && [ "${#input_command}" -gt 8 ] && echo "${input_command%% *}" || echo "${input_command}" + if [[ "${SHORT_TERM_LINE:-}" == true && "${#input_command}" -gt 8 ]]; then + echo "${input_command%% *}" + else + echo "${input_command}" + fi } set_xterm_title() { - local title="$1" - echo -ne "\033]0;$title\007" + local title="${1:-}" + echo -ne "\033]0;${title}\007" } precmd_xterm_title() { - set_xterm_title "${SHORT_USER:-${USER}}@${SHORT_HOSTNAME:-${HOSTNAME}} $(_short-dirname) $PROMPT_CHAR" + set_xterm_title "${SHORT_USER:-${USER}}@${SHORT_HOSTNAME:-${HOSTNAME}} $(_short-dirname) ${PROMPT_CHAR:-\$}" } preexec_xterm_title() { - set_xterm_title "$(_short-command "${1}") {$(_short-dirname)} (${SHORT_USER:-${USER}}@${SHORT_HOSTNAME:-${HOSTNAME}})" + local command_line="${BASH_COMMAND:-${1:-}}" + local directory_name short_command + directory_name="$(_short-dirname)" + short_command="$(_short-command "${command_line}")" + set_xterm_title "${short_command} {${directory_name}} (${SHORT_USER:-${USER}}@${SHORT_HOSTNAME:-${HOSTNAME}})" } -case "$TERM" in +case "${TERM:-dumb}" in xterm* | rxvt*) precmd_functions+=(precmd_xterm_title) preexec_functions+=(preexec_xterm_title) diff --git a/plugins/available/z_autoenv.plugin.bash b/plugins/available/z_autoenv.plugin.bash index 553a7ba3..a2f97d28 100644 --- a/plugins/available/z_autoenv.plugin.bash +++ b/plugins/available/z_autoenv.plugin.bash @@ -11,7 +11,7 @@ autoenv_init() typeset target home _file typeset -a _files target=$1 - home="$(dirname "$HOME")" + home="${HOME%/*}" _files=( $( while [[ "$PWD" != "/" && "$PWD" != "$home" ]] diff --git a/plugins/available/zoxide.plugin.bash b/plugins/available/zoxide.plugin.bash new file mode 100644 index 00000000..5f9d634f --- /dev/null +++ b/plugins/available/zoxide.plugin.bash @@ -0,0 +1,9 @@ +# shellcheck shell=bash +cite about-plugin +about-plugin 'zoxide is a smarter cd command for your shell.' + +if _command_exists zoxide; then + eval "$(zoxide init bash)" +else + _log_error 'zoxide not found, please install it from https://github.com/ajeetdsouza/zoxide' +fi diff --git a/profiles/default.bash_it b/profiles/default.bash_it new file mode 100644 index 00000000..9a55f6c7 --- /dev/null +++ b/profiles/default.bash_it @@ -0,0 +1,12 @@ +# This is the default profile of Bash-it + +# plugins +plugins base + +# completion +completion aliases +completion bash-it +completion system + +# aliases +aliases general diff --git a/scripts/reloader.bash b/scripts/reloader.bash index f22de67e..846bbf75 100644 --- a/scripts/reloader.bash +++ b/scripts/reloader.bash @@ -1,52 +1,52 @@ -#!/bin/bash +# shellcheck shell=bash +# +# The core component loader. + +# shellcheck disable=SC2034 BASH_IT_LOG_PREFIX="core: reloader: " +_bash_it_reloader_type="" -function _set-prefix-based-on-path() -{ - filename=$(_bash-it-get-component-name-from-path "$1") - extension=$(_bash-it-get-component-type-from-path "$1") - BASH_IT_LOG_PREFIX="$extension: $filename: " -} +if [[ "${1:-}" != "skip" ]] && [[ -d "${BASH_IT?}/enabled" ]]; then + case $1 in + alias | completion | plugin) + _bash_it_reloader_type=$1 + _log_debug "Loading enabled $1 components..." + ;; + '' | *) + _log_debug "Loading all enabled components..." + ;; + esac -if [[ "$1" != "skip" ]] && [[ -d "$BASH_IT/enabled" ]]; then - _bash_it_config_type="" - - case $1 in - alias|completion|plugin) - _bash_it_config_type=$1 - _log_debug "Loading enabled $1 components..." ;; - *|'') - _log_debug "Loading all enabled components..." ;; - esac - - for _bash_it_config_file in $(sort <(compgen -G "$BASH_IT/enabled/*${_bash_it_config_type}.bash")); do - if [ -e "${_bash_it_config_file}" ]; then - _set-prefix-based-on-path "${_bash_it_config_file}" - _log_debug "Loading component..." - # shellcheck source=/dev/null - source $_bash_it_config_file - else - echo "Unable to read ${_bash_it_config_file}" > /dev/stderr - fi - done + for _bash_it_reloader_file in "$BASH_IT/enabled"/*"${_bash_it_reloader_type}.bash"; do + if [[ -e "${_bash_it_reloader_file}" ]]; then + _bash-it-log-prefix-by-path "${_bash_it_reloader_file}" + _log_debug "Loading component..." + # shellcheck source=/dev/null + source "$_bash_it_reloader_file" + _log_debug "Loaded." + else + _log_error "Unable to read ${_bash_it_reloader_file}" + fi + done fi -if [[ -n "${2}" ]] && [[ -d "$BASH_IT/${2}/enabled" ]]; then - case $2 in - aliases|completion|plugins) - _log_warning "Using legacy enabling for $2, please update your bash-it version and migrate" - for _bash_it_config_file in $(sort <(compgen -G "$BASH_IT/${2}/enabled/*.bash")); do - if [[ -e "$_bash_it_config_file" ]]; then - _set-prefix-based-on-path "${_bash_it_config_file}" - _log_debug "Loading component..." - # shellcheck source=/dev/null - source "$_bash_it_config_file" - else - echo "Unable to locate ${_bash_it_config_file}" > /dev/stderr - fi - done ;; - esac +if [[ -n "${2:-}" ]] && [[ -d "$BASH_IT/${2}/enabled" ]]; then + case $2 in + aliases | completion | plugins) + _log_warning "Using legacy enabling for $2, please update your bash-it version and migrate" + for _bash_it_reloader_file in "$BASH_IT/${2}/enabled"/*.bash; do + if [[ -e "$_bash_it_reloader_file" ]]; then + _bash-it-log-prefix-by-path "${_bash_it_reloader_file}" + _log_debug "Loading component..." + # shellcheck source=/dev/null + source "$_bash_it_reloader_file" + _log_debug "Loaded." + else + _log_error "Unable to locate ${_bash_it_reloader_file}" + fi + done + ;; + esac fi -unset _bash_it_config_file -unset _bash_it_config_type +unset "${!_bash_it_reloader_@}" diff --git a/template/bash_profile.template.bash b/template/bash_profile.template.bash index 75febdab..3def2866 100755 --- a/template/bash_profile.template.bash +++ b/template/bash_profile.template.bash @@ -14,6 +14,10 @@ export BASH_IT="{{BASH_IT}}" # location /.bash_it/themes/ export BASH_IT_THEME='bobby' +# Some themes can show whether `sudo` has a current token or not. +# Set `$THEME_CHECK_SUDO` to `true` to check every prompt: +#THEME_CHECK_SUDO='true' + # (Advanced): Change this to the name of your remote repo if you # cloned bash-it with a remote other than origin such as `bash-it`. # export BASH_IT_REMOTE='bash-it' @@ -34,6 +38,9 @@ export IRC_CLIENT='irssi' # Set this to the command you use for todo.txt-cli export TODO="t" +# Set this to the location of your work or project folders +#BASH_IT_PROJECT_PATHS="${HOME}/Projects:/Volumes/work/src" + # Set this to false to turn off version control status checking within the prompt for all themes export SCM_CHECK=true # Set to actual location of gitstatus directory if installed diff --git a/test/bash_it/bash_it.bats b/test/bash_it/bash_it.bats index a692909e..ef3cdbab 100644 --- a/test/bash_it/bash_it.bats +++ b/test/bash_it/bash_it.bats @@ -1,20 +1,11 @@ -#!/usr/bin/env bats +# shellcheck shell=bats -load ../test_helper -load "${BASH_IT}/vendor/github.com/erichs/composure/composure.sh" - -function local_setup { - setup_test_fixture +load "${MAIN_BASH_IT_DIR?}/test/test_helper.bash" +function local_setup_file() { # Copy the test fixture to the Bash-it folder - if command -v rsync &> /dev/null - then - rsync -a "$BASH_IT/test/fixtures/bash_it/" "$BASH_IT/" - else - find "$BASH_IT/test/fixtures/bash_it" \ - -mindepth 1 -maxdepth 1 \ - -exec cp -r {} "$BASH_IT/" \; - fi + cp -fRP "${BASH_IT?}/test/fixtures/bash_it"/* "${BASH_IT?}/" || true + # don't load any libraries as the tests here test the *whole* kit } @test "bash-it: verify that the test fixture is available" { diff --git a/test/plugins/alias-completion.plugin.bats b/test/completion/aliases.completion.bats similarity index 62% rename from test/plugins/alias-completion.plugin.bats rename to test/completion/aliases.completion.bats index 497dded9..83ae947a 100644 --- a/test/plugins/alias-completion.plugin.bats +++ b/test/completion/aliases.completion.bats @@ -1,31 +1,30 @@ -#!/usr/bin/env bats +# shellcheck shell=bats -load ../test_helper -load "${BASH_IT}/vendor/github.com/erichs/composure/composure.sh" -load ../../lib/log -load ../../lib/helpers +load "${MAIN_BASH_IT_DIR?}/test/test_helper.bash" -cite _about _param _example _group _author _version - -load ../../completion/available/capistrano.completion +function local_setup_file() { + setup_libs "helpers" + # Load something, anything... + load ../../completion/available/capistrano.completion +} @test "alias-completion: See that aliases with double quotes and brackets do not break the plugin" { alias gtest="git log --graph --pretty=format:'%C(bold)%h%Creset%C(magenta)%d%Creset %s %C(yellow)<%an> %C(cyan)(%cr)%Creset' --abbrev-commit --date=relative" - load ../../plugins/available/alias-completion.plugin + run load "${BASH_IT?}/completion/available/aliases.completion.bash" assert_success } @test "alias-completion: See that aliases with single quotes and brackets do not break the plugin" { alias gtest='git log --graph --pretty=format:"%C(bold)%h%Creset%C(magenta)%d%Creset %s %C(yellow)<%an> %C(cyan)(%cr)%Creset" --abbrev-commit --date=relative' - load ../../plugins/available/alias-completion.plugin + run load "${BASH_IT?}/completion/available/aliases.completion.bash" assert_success } @test "alias-completion: See that having aliased rm command does not output unnecessary output" { alias rm='rm -v' - load ../../plugins/available/alias-completion.plugin + run load "${BASH_IT?}/completion/available/aliases.completion.bash" refute_output } diff --git a/test/completion/bash-it.completion.bats b/test/completion/bash-it.completion.bats old mode 100644 new mode 100755 index eaa4423d..29d1dc94 --- a/test/completion/bash-it.completion.bats +++ b/test/completion/bash-it.completion.bats @@ -1,16 +1,16 @@ -#!/usr/bin/env bats +# shellcheck shell=bats -load ../test_helper -load "${BASH_IT}/vendor/github.com/erichs/composure/composure.sh" -load ../../completion/available/bash-it.completion +load "${MAIN_BASH_IT_DIR?}/test/test_helper.bash" -function local_setup { - setup_test_fixture +function local_setup_file() { + setup_libs "helpers" + load "${BASH_IT?}/completion/available/bash-it.completion.bash" } -@test "completion bash-it: ensure that the _bash-it-comp function is available" { - type -a _bash-it-comp &> /dev/null +@test "completion bash-it: ensure that the _bash-it function is available" { + run type -t _bash-it assert_success + assert_output "function" } function __check_completion () { @@ -37,7 +37,7 @@ function __check_completion () { COMP_CWORD=$(( ${#COMP_WORDS[@]} - 1 )) # Run the Bash-it completion function - _bash-it-comp + _bash-it # Return the completion output echo "${COMPREPLY[@]}" @@ -80,32 +80,42 @@ function __check_completion () { @test "completion bash-it: show options" { run __check_completion 'bash-it ' - assert_line -n 0 "disable enable help migrate reload restart doctor search show update version" + assert_line -n 0 "disable enable help migrate reload restart preview profile doctor search show update version" } @test "completion bash-it: bash-ti - show options" { run __check_completion 'bash-ti ' - assert_line -n 0 "disable enable help migrate reload restart doctor search show update version" + assert_line -n 0 "disable enable help migrate reload restart preview profile doctor search show update version" } @test "completion bash-it: shit - show options" { run __check_completion 'shit ' - assert_line -n 0 "disable enable help migrate reload restart doctor search show update version" + assert_line -n 0 "disable enable help migrate reload restart preview profile doctor search show update version" } @test "completion bash-it: bashit - show options" { run __check_completion 'bashit ' - assert_line -n 0 "disable enable help migrate reload restart doctor search show update version" + assert_line -n 0 "disable enable help migrate reload restart preview profile doctor search show update version" } @test "completion bash-it: batshit - show options" { run __check_completion 'batshit ' - assert_line -n 0 "disable enable help migrate reload restart doctor search show update version" + assert_line -n 0 "disable enable help migrate reload restart preview profile doctor search show update version" } @test "completion bash-it: bash_it - show options" { run __check_completion 'bash_it ' - assert_line -n 0 "disable enable help migrate reload restart doctor search show update version" + assert_line -n 0 "disable enable help migrate reload restart preview profile doctor search show update version" +} + +@test "completion bash-it: profile - show options" { + run __check_completion 'bash-it profile ' + assert_line -n 0 "load save list rm" +} + +@test "completion bash-it: profile load - show options" { + run __check_completion 'bash-it profile load ' + assert_line -n 0 "default" } @test "completion bash-it: show - show options" { @@ -144,10 +154,10 @@ function __check_completion () { } @test "completion bash-it: disable - provide the a* aliases when atom is enabled with the old location and name" { - ln -s $BASH_IT/aliases/available/atom.aliases.bash $BASH_IT/aliases/enabled/atom.aliases.bash + ln -s "$BASH_IT/aliases/available/atom.aliases.bash" "$BASH_IT/aliases/enabled/atom.aliases.bash" assert_link_exist "$BASH_IT/aliases/enabled/atom.aliases.bash" - ln -s $BASH_IT/completion/available/apm.completion.bash $BASH_IT/completion/enabled/apm.completion.bash + ln -s "$BASH_IT/completion/available/apm.completion.bash" "$BASH_IT/completion/enabled/apm.completion.bash" assert_link_exist "$BASH_IT/completion/enabled/apm.completion.bash" run __check_completion 'bash-it disable alias a' @@ -155,10 +165,10 @@ function __check_completion () { } @test "completion bash-it: disable - provide the a* aliases when atom is enabled with the old location and priority-based name" { - ln -s $BASH_IT/aliases/available/atom.aliases.bash $BASH_IT/aliases/enabled/150---atom.aliases.bash + ln -s "$BASH_IT/aliases/available/atom.aliases.bash" "$BASH_IT/aliases/enabled/150---atom.aliases.bash" assert_link_exist "$BASH_IT/aliases/enabled/150---atom.aliases.bash" - ln -s $BASH_IT/completion/available/apm.completion.bash $BASH_IT/completion/enabled/350---apm.completion.bash + ln -s "$BASH_IT/completion/available/apm.completion.bash" "$BASH_IT/completion/enabled/350---apm.completion.bash" assert_link_exist "$BASH_IT/completion/enabled/350---apm.completion.bash" run __check_completion 'bash-it disable alias a' @@ -166,10 +176,10 @@ function __check_completion () { } @test "completion bash-it: disable - provide the a* aliases when atom is enabled with the new location and priority-based name" { - ln -s $BASH_IT/aliases/available/atom.aliases.bash $BASH_IT/enabled/150---atom.aliases.bash + ln -s "$BASH_IT/aliases/available/atom.aliases.bash" "$BASH_IT/enabled/150---atom.aliases.bash" assert_link_exist "$BASH_IT/enabled/150---atom.aliases.bash" - ln -s $BASH_IT/completion/available/apm.completion.bash $BASH_IT/enabled/350---apm.completion.bash + ln -s "$BASH_IT/completion/available/apm.completion.bash" "$BASH_IT/enabled/350---apm.completion.bash" assert_link_exist "$BASH_IT/enabled/350---apm.completion.bash" run __check_completion 'bash-it disable alias a' @@ -177,10 +187,10 @@ function __check_completion () { } @test "completion bash-it: disable - provide the docker-machine plugin when docker-machine is enabled with the old location and name" { - ln -s $BASH_IT/aliases/available/docker-compose.aliases.bash $BASH_IT/aliases/enabled/docker-compose.aliases.bash + ln -s "$BASH_IT/aliases/available/docker-compose.aliases.bash" "$BASH_IT/aliases/enabled/docker-compose.aliases.bash" assert_link_exist "$BASH_IT/aliases/enabled/docker-compose.aliases.bash" - ln -s $BASH_IT/plugins/available/docker-machine.plugin.bash $BASH_IT/plugins/enabled/docker-machine.plugin.bash + ln -s "$BASH_IT/plugins/available/docker-machine.plugin.bash" "$BASH_IT/plugins/enabled/docker-machine.plugin.bash" assert_link_exist "$BASH_IT/plugins/enabled/docker-machine.plugin.bash" run __check_completion 'bash-it disable plugin docker' @@ -188,10 +198,10 @@ function __check_completion () { } @test "completion bash-it: disable - provide the docker-machine plugin when docker-machine is enabled with the old location and priority-based name" { - ln -s $BASH_IT/aliases/available/docker-compose.aliases.bash $BASH_IT/aliases/enabled/150---docker-compose.aliases.bash + ln -s "$BASH_IT/aliases/available/docker-compose.aliases.bash" "$BASH_IT/aliases/enabled/150---docker-compose.aliases.bash" assert_link_exist "$BASH_IT/aliases/enabled/150---docker-compose.aliases.bash" - ln -s $BASH_IT/plugins/available/docker-machine.plugin.bash $BASH_IT/plugins/enabled/350---docker-machine.plugin.bash + ln -s "$BASH_IT/plugins/available/docker-machine.plugin.bash" "$BASH_IT/plugins/enabled/350---docker-machine.plugin.bash" assert_link_exist "$BASH_IT/plugins/enabled/350---docker-machine.plugin.bash" run __check_completion 'bash-it disable plugin docker' @@ -199,10 +209,10 @@ function __check_completion () { } @test "completion bash-it: disable - provide the docker-machine plugin when docker-machine is enabled with the new location and priority-based name" { - ln -s $BASH_IT/aliases/available/docker-compose.aliases.bash $BASH_IT/enabled/150---docker-compose.aliases.bash + ln -s "$BASH_IT/aliases/available/docker-compose.aliases.bash" "$BASH_IT/enabled/150---docker-compose.aliases.bash" assert_link_exist "$BASH_IT/enabled/150---docker-compose.aliases.bash" - ln -s $BASH_IT/plugins/available/docker-machine.plugin.bash $BASH_IT/enabled/350---docker-machine.plugin.bash + ln -s "$BASH_IT/plugins/available/docker-machine.plugin.bash" "$BASH_IT/enabled/350---docker-machine.plugin.bash" assert_link_exist "$BASH_IT/enabled/350---docker-machine.plugin.bash" run __check_completion 'bash-it disable plugin docker' @@ -210,10 +220,10 @@ function __check_completion () { } @test "completion bash-it: disable - provide the todo.txt-cli aliases when todo plugin is enabled with the old location and name" { - ln -s $BASH_IT/aliases/available/todo.txt-cli.aliases.bash $BASH_IT/aliases/enabled/todo.txt-cli.aliases.bash + ln -s "$BASH_IT/aliases/available/todo.txt-cli.aliases.bash" "$BASH_IT/aliases/enabled/todo.txt-cli.aliases.bash" assert_link_exist "$BASH_IT/aliases/enabled/todo.txt-cli.aliases.bash" - ln -s $BASH_IT/plugins/available/todo.plugin.bash $BASH_IT/plugins/enabled/todo.plugin.bash + ln -s "$BASH_IT/plugins/available/todo.plugin.bash" "$BASH_IT/plugins/enabled/todo.plugin.bash" assert_link_exist "$BASH_IT/plugins/enabled/todo.plugin.bash" run __check_completion 'bash-it disable alias to' @@ -221,10 +231,10 @@ function __check_completion () { } @test "completion bash-it: disable - provide the todo.txt-cli aliases when todo plugin is enabled with the old location and priority-based name" { - ln -s $BASH_IT/aliases/available/todo.txt-cli.aliases.bash $BASH_IT/aliases/enabled/150---todo.txt-cli.aliases.bash + ln -s "$BASH_IT/aliases/available/todo.txt-cli.aliases.bash" "$BASH_IT/aliases/enabled/150---todo.txt-cli.aliases.bash" assert_link_exist "$BASH_IT/aliases/enabled/150---todo.txt-cli.aliases.bash" - ln -s $BASH_IT/plugins/available/todo.plugin.bash $BASH_IT/plugins/enabled/350---todo.plugin.bash + ln -s "$BASH_IT/plugins/available/todo.plugin.bash" "$BASH_IT/plugins/enabled/350---todo.plugin.bash" assert_link_exist "$BASH_IT/plugins/enabled/350---todo.plugin.bash" run __check_completion 'bash-it disable alias to' @@ -232,10 +242,10 @@ function __check_completion () { } @test "completion bash-it: disable - provide the todo.txt-cli aliases when todo plugin is enabled with the new location and priority-based name" { - ln -s $BASH_IT/aliases/available/todo.txt-cli.aliases.bash $BASH_IT/enabled/150---todo.txt-cli.aliases.bash + ln -s "$BASH_IT/aliases/available/todo.txt-cli.aliases.bash" "$BASH_IT/enabled/150---todo.txt-cli.aliases.bash" assert_link_exist "$BASH_IT/enabled/150---todo.txt-cli.aliases.bash" - ln -s $BASH_IT/plugins/available/todo.plugin.bash $BASH_IT/enabled/350---todo.plugin.bash + ln -s "$BASH_IT/plugins/available/todo.plugin.bash" "$BASH_IT/enabled/350---todo.plugin.bash" assert_link_exist "$BASH_IT/enabled/350---todo.plugin.bash" run __check_completion 'bash-it disable alias to' @@ -253,7 +263,7 @@ function __check_completion () { } @test "completion bash-it: enable - provide the a* aliases when atom is enabled with the old location and name" { - ln -s $BASH_IT/aliases/available/atom.aliases.bash $BASH_IT/aliases/enabled/atom.aliases.bash + ln -s "$BASH_IT/aliases/available/atom.aliases.bash" "$BASH_IT/aliases/enabled/atom.aliases.bash" assert_link_exist "$BASH_IT/aliases/enabled/atom.aliases.bash" run __check_completion 'bash-it enable alias a' @@ -261,7 +271,7 @@ function __check_completion () { } @test "completion bash-it: enable - provide the a* aliases when atom is enabled with the old location and priority-based name" { - ln -s $BASH_IT/aliases/available/atom.aliases.bash $BASH_IT/aliases/enabled/150---atom.aliases.bash + ln -s "$BASH_IT/aliases/available/atom.aliases.bash" "$BASH_IT/aliases/enabled/150---atom.aliases.bash" assert_link_exist "$BASH_IT/aliases/enabled/150---atom.aliases.bash" run __check_completion 'bash-it enable alias a' @@ -269,55 +279,55 @@ function __check_completion () { } @test "completion bash-it: enable - provide the a* aliases when atom is enabled with the new location and priority-based name" { - ln -s $BASH_IT/aliases/available/atom.aliases.bash $BASH_IT/enabled/150---atom.aliases.bash + ln -s "$BASH_IT/aliases/available/atom.aliases.bash" "$BASH_IT/enabled/150---atom.aliases.bash" assert_link_exist "$BASH_IT/enabled/150---atom.aliases.bash" run __check_completion 'bash-it enable alias a' assert_line -n 0 "all ag ansible apt" } -@test "completion bash-it: enable - provide the docker-* plugins when nothing is enabled with the old location and name" { - ln -s $BASH_IT/aliases/available/docker-compose.aliases.bash $BASH_IT/aliases/enabled/docker-compose.aliases.bash +@test "completion bash-it: enable - provide the docker* plugins when docker-compose is enabled with the old location and name" { + ln -s "$BASH_IT/aliases/available/docker-compose.aliases.bash" "$BASH_IT/aliases/enabled/docker-compose.aliases.bash" assert_link_exist "$BASH_IT/aliases/enabled/docker-compose.aliases.bash" run __check_completion 'bash-it enable plugin docker' - assert_line -n 0 "docker-compose docker-machine docker" + assert_line -n 0 "docker docker-compose docker-machine" } -@test "completion bash-it: enable - provide the docker-* plugins when nothing is enabled with the old location and priority-based name" { - ln -s $BASH_IT/aliases/available/docker-compose.aliases.bash $BASH_IT/aliases/enabled/150---docker-compose.aliases.bash +@test "completion bash-it: enable - provide the docker-* plugins when docker-compose is enabled with the old location and priority-based name" { + ln -s "$BASH_IT/aliases/available/docker-compose.aliases.bash" "$BASH_IT/aliases/enabled/150---docker-compose.aliases.bash" assert_link_exist "$BASH_IT/aliases/enabled/150---docker-compose.aliases.bash" run __check_completion 'bash-it enable plugin docker' - assert_line -n 0 "docker-compose docker-machine docker" + assert_line -n 0 "docker docker-compose docker-machine" } -@test "completion bash-it: enable - provide the docker-* plugins when nothing is enabled with the new location and priority-based name" { - ln -s $BASH_IT/aliases/available/docker-compose.aliases.bash $BASH_IT/enabled/150---docker-compose.aliases.bash +@test "completion bash-it: enable - provide the docker-* plugins when docker-compose is enabled with the new location and priority-based name" { + ln -s "$BASH_IT/aliases/available/docker-compose.aliases.bash" "$BASH_IT/enabled/150---docker-compose.aliases.bash" assert_link_exist "$BASH_IT/enabled/150---docker-compose.aliases.bash" run __check_completion 'bash-it enable plugin docker' - assert_line -n 0 "docker-compose docker-machine docker" + assert_line -n 0 "docker docker-compose docker-machine" } -@test "completion bash-it: enable - provide the docker-* completions when nothing is enabled with the old location and name" { - ln -s $BASH_IT/aliases/available/docker-compose.aliases.bash $BASH_IT/aliases/enabled/docker-compose.aliases.bash +@test "completion bash-it: enable - provide the docker* completions when docker-compose is enabled with the old location and name" { + ln -s "$BASH_IT/aliases/available/docker-compose.aliases.bash" "$BASH_IT/aliases/enabled/docker-compose.aliases.bash" assert_link_exist "$BASH_IT/aliases/enabled/docker-compose.aliases.bash" run __check_completion 'bash-it enable completion docker' assert_line -n 0 "docker docker-compose docker-machine" } -@test "completion bash-it: enable - provide the docker-* completions when nothing is enabled with the old location and priority-based name" { - ln -s $BASH_IT/aliases/available/docker-compose.aliases.bash $BASH_IT/aliases/enabled/150---docker-compose.aliases.bash +@test "completion bash-it: enable - provide the docker* completions when docker-compose is enabled with the old location and priority-based name" { + ln -s "$BASH_IT/aliases/available/docker-compose.aliases.bash" "$BASH_IT/aliases/enabled/150---docker-compose.aliases.bash" assert_link_exist "$BASH_IT/aliases/enabled/150---docker-compose.aliases.bash" run __check_completion 'bash-it enable completion docker' assert_line -n 0 "docker docker-compose docker-machine" } -@test "completion bash-it: enable - provide the docker-* completions when nothing is enabled with the new location and priority-based name" { - ln -s $BASH_IT/aliases/available/docker-compose.aliases.bash $BASH_IT/enabled/150---docker-compose.aliases.bash +@test "completion bash-it: enable - provide the docker* completions when docker-compose is enabled with the new location and priority-based name" { + ln -s "$BASH_IT/aliases/available/docker-compose.aliases.bash" "$BASH_IT/enabled/150---docker-compose.aliases.bash" assert_link_exist "$BASH_IT/enabled/150---docker-compose.aliases.bash" run __check_completion 'bash-it enable completion docker' @@ -325,7 +335,7 @@ function __check_completion () { } @test "completion bash-it: enable - provide the todo.txt-cli aliases when todo plugin is enabled with the old location and name" { - ln -s $BASH_IT/plugins/available/todo.plugin.bash $BASH_IT/plugins/enabled/todo.plugin.bash + ln -s "$BASH_IT/plugins/available/todo.plugin.bash" "$BASH_IT/plugins/enabled/todo.plugin.bash" assert_link_exist "$BASH_IT/plugins/enabled/todo.plugin.bash" run __check_completion 'bash-it enable alias to' @@ -333,7 +343,7 @@ function __check_completion () { } @test "completion bash-it: enable - provide the todo.txt-cli aliases when todo plugin is enabled with the old location and priority-based name" { - ln -s $BASH_IT/plugins/available/todo.plugin.bash $BASH_IT/plugins/enabled/350---todo.plugin.bash + ln -s "$BASH_IT/plugins/available/todo.plugin.bash" "$BASH_IT/plugins/enabled/350---todo.plugin.bash" assert_link_exist "$BASH_IT/plugins/enabled/350---todo.plugin.bash" run __check_completion 'bash-it enable alias to' @@ -341,7 +351,7 @@ function __check_completion () { } @test "completion bash-it: enable - provide the todo.txt-cli aliases when todo plugin is enabled with the new location and priority-based name" { - ln -s $BASH_IT/plugins/available/todo.plugin.bash $BASH_IT/enabled/350---todo.plugin.bash + ln -s "$BASH_IT/plugins/available/todo.plugin.bash" "$BASH_IT/enabled/350---todo.plugin.bash" assert_link_exist "$BASH_IT/enabled/350---todo.plugin.bash" run __check_completion 'bash-it enable alias to' diff --git a/test/fixtures/bash_it/profiles/test-bad-component.bash_it b/test/fixtures/bash_it/profiles/test-bad-component.bash_it new file mode 100644 index 00000000..068c4b63 --- /dev/null +++ b/test/fixtures/bash_it/profiles/test-bad-component.bash_it @@ -0,0 +1,12 @@ +# plugins +plugins base + +# completion +completion aliases +completion bash-it +completion system + +# aliases +aliases general +# Bad component +aliases bla diff --git a/test/fixtures/bash_it/profiles/test-bad-type.bash_it b/test/fixtures/bash_it/profiles/test-bad-type.bash_it new file mode 100644 index 00000000..102c52ea --- /dev/null +++ b/test/fixtures/bash_it/profiles/test-bad-type.bash_it @@ -0,0 +1,12 @@ +# plugins +plugins base +# Bad type +compleetion aliases + +# completion +completion aliases +completion bash-it +completion system + +# aliases +aliases general diff --git a/test/fixtures/go/go path/bin/.keep b/test/fixtures/go/go path/bin/.keep new file mode 100644 index 00000000..e69de29b diff --git a/test/fixtures/go/gopath/bin/.keep b/test/fixtures/go/gopath/bin/.keep new file mode 100644 index 00000000..e69de29b diff --git a/test/fixtures/go/gopath2/bin/.keep b/test/fixtures/go/gopath2/bin/.keep new file mode 100644 index 00000000..e69de29b diff --git a/test/fixtures/svn/broken/xcrun b/test/fixtures/svn/broken/xcrun new file mode 100755 index 00000000..e69de29b diff --git a/test/fixtures/svn/working/xcrun b/test/fixtures/svn/working/xcrun new file mode 100755 index 00000000..e69de29b diff --git a/test/install/install.bats b/test/install/install.bats index 40f3162d..c9e1794c 100644 --- a/test/install/install.bats +++ b/test/install/install.bats @@ -1,20 +1,22 @@ -#!/usr/bin/env bats +# shellcheck shell=bats -load ../test_helper -load "${BASH_IT}/vendor/github.com/erichs/composure/composure.sh" +load "${MAIN_BASH_IT_DIR?}/test/test_helper.bash" -# Determine which config file to use based on OS. -case $OSTYPE in - darwin*) - export BASH_IT_CONFIG_FILE=.bash_profile - ;; - *) - export BASH_IT_CONFIG_FILE=.bashrc - ;; -esac +function local_setup() { + export HOME="$BATS_TEST_TMPDIR" +} -function local_setup { - setup_test_fixture +function local_setup_file() { + # Determine which config file to use based on OS. + case $OSTYPE in + darwin*) + export BASH_IT_CONFIG_FILE=.bash_profile + ;; + *) + export BASH_IT_CONFIG_FILE=.bashrc + ;; + esac + # don't load any libraries as the tests here test the *whole* kit } @test "install: verify that the install script exists" { @@ -26,28 +28,28 @@ function local_setup { ./install.sh --silent - assert_file_exist "$BASH_IT_TEST_HOME/$BASH_IT_CONFIG_FILE" + assert_file_exist "$HOME/$BASH_IT_CONFIG_FILE" assert_link_exist "$BASH_IT/enabled/150---general.aliases.bash" assert_link_exist "$BASH_IT/enabled/250---base.plugin.bash" - assert_link_exist "$BASH_IT/enabled/365---alias-completion.plugin.bash" + assert_link_exist "$BASH_IT/enabled/800---aliases.completion.bash" assert_link_exist "$BASH_IT/enabled/350---bash-it.completion.bash" - assert_link_exist "$BASH_IT/enabled/350---system.completion.bash" + assert_link_exist "$BASH_IT/enabled/325---system.completion.bash" } @test "install: verify that a backup file is created" { cd "$BASH_IT" - touch "$BASH_IT_TEST_HOME/$BASH_IT_CONFIG_FILE" - echo "test file content" > "$BASH_IT_TEST_HOME/$BASH_IT_CONFIG_FILE" - local md5_orig=$(md5sum "$BASH_IT_TEST_HOME/$BASH_IT_CONFIG_FILE" | awk '{print $1}') + touch "$HOME/$BASH_IT_CONFIG_FILE" + echo "test file content" > "$HOME/$BASH_IT_CONFIG_FILE" + local md5_orig=$(md5sum "$HOME/$BASH_IT_CONFIG_FILE" | awk '{print $1}') ./install.sh --silent - assert_file_exist "$BASH_IT_TEST_HOME/$BASH_IT_CONFIG_FILE" - assert_file_exist "$BASH_IT_TEST_HOME/$BASH_IT_CONFIG_FILE.bak" + assert_file_exist "$HOME/$BASH_IT_CONFIG_FILE" + assert_file_exist "$HOME/$BASH_IT_CONFIG_FILE.bak" - local md5_bak=$(md5sum "$BASH_IT_TEST_HOME/$BASH_IT_CONFIG_FILE.bak" | awk '{print $1}') + local md5_bak=$(md5sum "$HOME/$BASH_IT_CONFIG_FILE.bak" | awk '{print $1}') assert_equal "$md5_orig" "$md5_bak" } @@ -71,15 +73,15 @@ function local_setup { @test "install: verify that the template is appended" { cd "$BASH_IT" - touch "$BASH_IT_TEST_HOME/$BASH_IT_CONFIG_FILE" - echo "test file content" > "$BASH_IT_TEST_HOME/$BASH_IT_CONFIG_FILE" + touch "$HOME/$BASH_IT_CONFIG_FILE" + echo "test file content" > "$HOME/$BASH_IT_CONFIG_FILE" ./install.sh --silent --append-to-config - assert_file_exist "$BASH_IT_TEST_HOME/$BASH_IT_CONFIG_FILE" - assert_file_exist "$BASH_IT_TEST_HOME/$BASH_IT_CONFIG_FILE.bak" + assert_file_exist "$HOME/$BASH_IT_CONFIG_FILE" + assert_file_exist "$HOME/$BASH_IT_CONFIG_FILE.bak" - run cat $BASH_IT_TEST_HOME/$BASH_IT_CONFIG_FILE + run cat "$HOME/$BASH_IT_CONFIG_FILE" assert_line "test file content" assert_line "source \"\$BASH_IT\"/bash_it.sh" diff --git a/test/install/uninstall.bats b/test/install/uninstall.bats index 038125b3..ab71a775 100644 --- a/test/install/uninstall.bats +++ b/test/install/uninstall.bats @@ -1,20 +1,22 @@ -#!/usr/bin/env bats +# shellcheck shell=bats -load ../test_helper -load "${BASH_IT}/vendor/github.com/erichs/composure/composure.sh" +load "${MAIN_BASH_IT_DIR?}/test/test_helper.bash" -# Determine which config file to use based on OS. -case $OSTYPE in - darwin*) - export BASH_IT_CONFIG_FILE=.bash_profile - ;; - *) - export BASH_IT_CONFIG_FILE=.bashrc - ;; -esac +function local_setup() { + export HOME="$BATS_TEST_TMPDIR" +} -function local_setup { - setup_test_fixture +function local_setup_file() { + # Determine which config file to use based on OS. + case $OSTYPE in + darwin*) + export BASH_IT_CONFIG_FILE=.bash_profile + ;; + *) + export BASH_IT_CONFIG_FILE=.bashrc + ;; + esac + # don't load any libraries as the tests here test the *whole* kit } @test "uninstall: verify that the uninstall script exists" { @@ -24,19 +26,18 @@ function local_setup { @test "uninstall: run the uninstall script with an existing backup file" { cd "$BASH_IT" - echo "test file content for backup" > "$BASH_IT_TEST_HOME/$BASH_IT_CONFIG_FILE.bak" - echo "test file content for original file" > "$BASH_IT_TEST_HOME/$BASH_IT_CONFIG_FILE" - local md5_bak=$(md5sum "$BASH_IT_TEST_HOME/$BASH_IT_CONFIG_FILE.bak" | awk '{print $1}') - - ./uninstall.sh + echo "test file content for backup" > "$HOME/$BASH_IT_CONFIG_FILE.bak" + echo "test file content for original file" > "$HOME/$BASH_IT_CONFIG_FILE" + local md5_bak=$(md5sum "$HOME/$BASH_IT_CONFIG_FILE.bak" | awk '{print $1}') + run ./uninstall.sh assert_success - assert_file_not_exist "$BASH_IT_TEST_HOME/$BASH_IT_CONFIG_FILE.uninstall" - assert_file_not_exist "$BASH_IT_TEST_HOME/$BASH_IT_CONFIG_FILE.bak" - assert_file_exist "$BASH_IT_TEST_HOME/$BASH_IT_CONFIG_FILE" + assert_file_not_exist "$HOME/$BASH_IT_CONFIG_FILE.uninstall" + assert_file_not_exist "$HOME/$BASH_IT_CONFIG_FILE.bak" + assert_file_exist "$HOME/$BASH_IT_CONFIG_FILE" - local md5_conf=$(md5sum "$BASH_IT_TEST_HOME/$BASH_IT_CONFIG_FILE" | awk '{print $1}') + local md5_conf=$(md5sum "$HOME/$BASH_IT_CONFIG_FILE" | awk '{print $1}') assert_equal "$md5_bak" "$md5_conf" } @@ -44,18 +45,17 @@ function local_setup { @test "uninstall: run the uninstall script without an existing backup file" { cd "$BASH_IT" - echo "test file content for original file" > "$BASH_IT_TEST_HOME/$BASH_IT_CONFIG_FILE" - local md5_orig=$(md5sum "$BASH_IT_TEST_HOME/$BASH_IT_CONFIG_FILE" | awk '{print $1}') - - ./uninstall.sh + echo "test file content for original file" > "$HOME/$BASH_IT_CONFIG_FILE" + local md5_orig=$(md5sum "$HOME/$BASH_IT_CONFIG_FILE" | awk '{print $1}') + run ./uninstall.sh assert_success - assert_file_exist "$BASH_IT_TEST_HOME/$BASH_IT_CONFIG_FILE.uninstall" - assert_file_not_exist "$BASH_IT_TEST_HOME/$BASH_IT_CONFIG_FILE.bak" - assert_file_not_exist "$BASH_IT_TEST_HOME/$BASH_IT_CONFIG_FILE" + assert_file_exist "$HOME/$BASH_IT_CONFIG_FILE.uninstall" + assert_file_not_exist "$HOME/$BASH_IT_CONFIG_FILE.bak" + assert_file_not_exist "$HOME/$BASH_IT_CONFIG_FILE" - local md5_uninstall=$(md5sum "$BASH_IT_TEST_HOME/$BASH_IT_CONFIG_FILE.uninstall" | awk '{print $1}') + local md5_uninstall=$(md5sum "$HOME/$BASH_IT_CONFIG_FILE.uninstall" | awk '{print $1}') assert_equal "$md5_orig" "$md5_uninstall" } diff --git a/test/lib/composure.bats b/test/lib/composure.bats old mode 100755 new mode 100644 index 02da83eb..01bfd967 --- a/test/lib/composure.bats +++ b/test/lib/composure.bats @@ -1,7 +1,11 @@ -#!/usr/bin/env bats +# shellcheck shell=bats -load ../test_helper -load "${BASH_IT}/vendor/github.com/erichs/composure/composure.sh" +load "${MAIN_BASH_IT_DIR?}/test/test_helper.bash" + +function local_setup_file() { + true + # don't load any libraries as the tests here test the *whole* kit +} @test "lib composure: _composure_keywords()" { run _composure_keywords diff --git a/test/lib/helpers.bats b/test/lib/helpers.bats index e60d5542..38a917fe 100644 --- a/test/lib/helpers.bats +++ b/test/lib/helpers.bats @@ -1,23 +1,24 @@ -#!/usr/bin/env bats +# shellcheck shell=bats -load ../test_helper -load "${BASH_IT}/vendor/github.com/erichs/composure/composure.sh" -load ../../lib/log -load ../../lib/utilities -load ../../lib/search -load ../../plugins/available/base.plugin +load "${MAIN_BASH_IT_DIR?}/test/test_helper.bash" -cite _about _param _example _group _author _version +function local_setup_file() { + setup_libs "colors" + load "${BASH_IT?}/plugins/available/base.plugin.bash" +} -load ../../lib/helpers - -function local_setup { - setup_test_fixture +function local_setup() { + # Copy the test fixture to the Bash-it folder + cp -RP "$BASH_IT/test/fixtures/bash_it"/* "$BASH_IT/" } # TODO Create global __is_enabled function # TODO Create global __get_base_name function # TODO Create global __get_enabled_name function +@test "bash-it: verify that the test fixture is available" { + assert_file_exist "$BASH_IT/profiles/test-bad-component.bash_it" + assert_file_exist "$BASH_IT/profiles/test-bad-type.bash_it" +} @test "helpers: _command_exists function exists" { run type -a _command_exists &> /dev/null @@ -283,6 +284,150 @@ function local_setup { assert_link_exist "$BASH_IT/enabled/225---nvm.plugin.bash" } +@test "helpers: profile load command sanity" { + run _bash-it-profile-load "default" + assert_success + + assert_link_exist "$BASH_IT/enabled/150---general.aliases.bash" + assert_link_exist "$BASH_IT/enabled/250---base.plugin.bash" + assert_link_exist "$BASH_IT/enabled/800---aliases.completion.bash" + assert_link_exist "$BASH_IT/enabled/350---bash-it.completion.bash" + assert_link_exist "$BASH_IT/enabled/325---system.completion.bash" +} + +@test "helpers: profile save command sanity" { + run _enable-plugin "nvm" + + run _bash-it-profile-save "test" + assert_line -n 0 "Saving plugins configuration..." + assert_line -n 1 "Saving completion configuration..." + assert_line -n 2 "Saving aliases configuration..." + assert_line -n 3 "All done!" + assert_file_exist "$BASH_IT/profiles/test.bash_it" +} + +@test "helpers: profile save creates valid file with only plugin enabled" { + run _enable-plugin "nvm" + + run _bash-it-profile-save "test" + run cat "$BASH_IT/profiles/test.bash_it" + assert_line -n 0 "# This file is auto generated by Bash-it. Do not edit manually!" + assert_line -n 1 "# plugins" + assert_line -n 2 "plugins nvm" +} + +@test "helpers: profile save creates valid file with only completion enabled" { + run _enable-completion "bash-it" + + run _bash-it-profile-save "test" + run cat "$BASH_IT/profiles/test.bash_it" + assert_line -n 0 "# This file is auto generated by Bash-it. Do not edit manually!" + assert_line -n 1 "# completion" + assert_line -n 2 "completion bash-it" +} + +@test "helpers: profile save creates valid file with only aliases enabled" { + run _enable-alias "general" + + run _bash-it-profile-save "test" + run cat "$BASH_IT/profiles/test.bash_it" + assert_line -n 0 "# This file is auto generated by Bash-it. Do not edit manually!" + assert_line -n 1 "# aliases" + assert_line -n 2 "aliases general" +} + +@test "helpers: profile edge case, empty configuration" { + run _bash-it-profile-save "test" + assert_line -n 3 "It seems like no configuration was enabled.." + assert_line -n 4 "Make sure to double check that this is the wanted behavior." + + run _enable-alias "general" + run _enable-plugin "base" + run _enable-plugin "alias-completion" + run _enable-completion "bash-it" + run _enable-completion "system" + + run _bash-it-profile-load "test" + assert_link_not_exist "$BASH_IT/enabled/150---general.aliases.bash" + assert_link_not_exist "$BASH_IT/enabled/250---base.plugin.bash" + assert_link_not_exist "$BASH_IT/enabled/800---aliases.completion.bash" + assert_link_not_exist "$BASH_IT/enabled/350---bash-it.completion.bash" + assert_link_not_exist "$BASH_IT/enabled/325---system.completion.bash" +} + +@test "helpers: profile save and load" { + run _enable-alias "general" + run _enable-plugin "base" + run _enable-plugin "alias-completion" + run _enable-completion "bash-it" + run _enable-completion "system" + + run _bash-it-profile-save "test" + assert_success + + run _disable-alias "general" + assert_link_not_exist "$BASH_IT/enabled/150---general.aliases.bash" + run _bash-it-profile-load "test" + assert_link_exist "$BASH_IT/enabled/150---general.aliases.bash" +} + +@test "helpers: profile load corrupted profile file: bad component" { + run _bash-it-profile-load "test-bad-component" + assert_line -n 1 -p "Bad line(#12) in profile, aborting load..." +} + +@test "helpers: profile load corrupted profile file: bad subdirectory" { + run _bash-it-profile-load "test-bad-type" + assert_line -n 1 -p "Bad line(#4) in profile, aborting load..." +} + +@test "helpers: profile rm sanity" { + run _bash-it-profile-save "test" + assert_file_exist "$BASH_IT/profiles/test.bash_it" + run _bash-it-profile-rm "test" + assert_line -n 0 "Removed profile 'test' successfully!" + assert_file_not_exist "$BASH_IT/profiles/test.bash_it" +} + +@test "helpers: profile rm no params" { + run _bash-it-profile-rm "" + assert_line -n 0 -p "Please specify profile name to remove..." +} + +@test "helpers: profile load no params" { + run _bash-it-profile-load "" + assert_line -n 0 -p "Please specify profile name to load, not changing configuration..." +} + +@test "helpers: profile rm default" { + run _bash-it-profile-rm "default" + assert_line -n 0 -p "Can not remove the default profile..." + assert_file_exist "$BASH_IT/profiles/default.bash_it" +} + +@test "helpers: profile rm bad profile name" { + run _bash-it-profile-rm "notexisting" + assert_line -n 0 -p "Could not find profile 'notexisting'..." +} + +@test "helpers: profile list sanity" { + run _bash-it-profile-list + assert_line -n 0 "Available profiles:" + assert_line -n 1 "default" +} + +@test "helpers: profile list more profiles" { + run _bash-it-profile-save "cactus" + run _bash-it-profile-save "another" + run _bash-it-profile-save "brother" + run _bash-it-profile-list + assert_line -n 0 "Available profiles:" + assert_line -n 4 "default" + assert_line -n 3 "cactus" + assert_line -n 1 "another" + assert_line -n 2 "brother" +} + @test "helpers: migrate plugins and completions that share the same name" { ln -s $BASH_IT/completion/available/dirs.completion.bash $BASH_IT/completion/enabled/350---dirs.completion.bash assert_link_exist "$BASH_IT/completion/enabled/350---dirs.completion.bash" @@ -583,5 +728,5 @@ function __migrate_all_components() { @test "helpers: describe the todo.txt-cli aliases without enabling them" { run _bash-it-aliases - assert_line "todo.txt-cli [ ] todo.txt-cli abbreviations" + assert_line "todo.txt-cli [ ] todo.txt-cli abbreviations" } diff --git a/test/lib/log.bats b/test/lib/log.bats index f4a04f0e..7d868fd6 100644 --- a/test/lib/log.bats +++ b/test/lib/log.bats @@ -1,12 +1,10 @@ -#!/usr/bin/env bats +# shellcheck shell=bats -load ../test_helper -load "${BASH_IT}/vendor/github.com/erichs/composure/composure.sh" -load ../../lib/appearance -load ../../plugins/available/base.plugin +load "${MAIN_BASH_IT_DIR?}/test/test_helper.bash" -cite _about _param _example _group _author _version -load ../../lib/log +function local_setup_file() { + setup_libs "log" +} @test "lib log: basic debug logging with BASH_IT_LOG_LEVEL_ALL" { BASH_IT_LOG_LEVEL=$BASH_IT_LOG_LEVEL_ALL diff --git a/test/lib/preexec.bats b/test/lib/preexec.bats new file mode 100644 index 00000000..10dc666d --- /dev/null +++ b/test/lib/preexec.bats @@ -0,0 +1,164 @@ +# shellcheck shell=bats + +load ../test_helper + +function local_setup { + setup_test_fixture + export __bp_enable_subshells=yas +} + +@test "vendor preexec: __bp_install_after_session_init() without existing" { + test_prompt_string="" + export PROMPT_COMMAND="$test_prompt_string" + + load ../../vendor/github.com/rcaloras/bash-preexec/bash-preexec.sh + assert_success + + assert_equal "${PROMPT_COMMAND}" $'__bp_trap_string="$(trap -p DEBUG)"\ntrap - DEBUG\n__bp_install' +} + +@test "vendor preexec: __bp_install_after_session_init() with existing" { + test_prompt_string="nah" + export PROMPT_COMMAND="$test_prompt_string" + + load ../../vendor/github.com/rcaloras/bash-preexec/bash-preexec.sh + assert_success + + assert_equal "${PROMPT_COMMAND}" "$test_prompt_string"$'\n__bp_trap_string="$(trap -p DEBUG)"\ntrap - DEBUG\n__bp_install' +} + +@test "vendor preexec: __bp_install_after_session_init() delayed" { + test_prompt_string="nah" + export PROMPT_COMMAND="$test_prompt_string" + export __bp_delay_install="blarg" + + load ../../vendor/github.com/rcaloras/bash-preexec/bash-preexec.sh + assert_success + + assert_equal "${PROMPT_COMMAND}" "$test_prompt_string" + + run __bp_install_after_session_init + assert_success + + __bp_install_after_session_init + assert_equal "${PROMPT_COMMAND}" "$test_prompt_string"$'\n__bp_trap_string="$(trap -p DEBUG)"\ntrap - DEBUG\n__bp_install' +} + +@test "vendor preexec: __bp_install() without existing" { + test_prompt_string="" + export PROMPT_COMMAND="$test_prompt_string" + + load ../../vendor/github.com/rcaloras/bash-preexec/bash-preexec.sh + assert_success + + run __bp_install + assert_success + + __bp_install + assert_equal "${PROMPT_COMMAND}" $'__bp_precmd_invoke_cmd\n__bp_interactive_mode' +} + +@test "vendor preexec: __bp_install() with existing" { + test_prompt_string="nah" + export PROMPT_COMMAND="$test_prompt_string" + + load ../../vendor/github.com/rcaloras/bash-preexec/bash-preexec.sh + assert_success + + run __bp_install + assert_success + + __bp_install + assert_equal "${PROMPT_COMMAND}" $'__bp_precmd_invoke_cmd\n'"$test_prompt_string"$'\n__bp_interactive_mode' +} + +@test "lib preexec: __bp_require_not_readonly()" { + run type -t __bp_require_not_readonly + assert_failure + + run load ../../lib/preexec.bash + assert_success + load ../../lib/preexec.bash + + run type -t __bp_require_not_readonly + assert_success + + export HISTCONTROL=blah:blah PROMPT_COMMAND="silly;rabbit" + readonly HISTCONTROL PROMPT_COMMAND + + run __bp_require_not_readonly + assert_success +} + +@test "lib preexec: __bp_adjust_histcontrol()" { + run type -t __bp_adjust_histcontrol + assert_failure + + run load ../../lib/preexec.bash + assert_success + load ../../lib/preexec.bash + + run type -t __bp_adjust_histcontrol + assert_success + + test_history_control_string="ignoreall:ignoredups:ignorespace:erasedups" + export HISTCONTROL="${test_history_control_string}" + + run __bp_adjust_histcontrol + assert_success + assert_equal "${HISTCONTROL}" "${test_history_control_string}" +} + +@test "lib preexec: __check_precmd_conflict()" { + test_precmd_function_name="test" + setup_libs "preexec" + + run __check_precmd_conflict "$test_precmd_function_name" + assert_failure + + export precmd_functions=("$test_precmd_function_name") + + run __check_precmd_conflict "$test_precmd_function_name" + assert_success +} + +@test "lib preexec: __check_preexec_conflict()" { + test_preexec_function_name="test" + setup_libs "preexec" + + run __check_preexec_conflict "$test_preexec_function_name" + assert_failure + + export preexec_functions=("$test_preexec_function_name") + + run __check_preexec_conflict "$test_preexec_function_name" + assert_success +} + +@test "lib preexec: safe_append_prompt_command()" { + test_precmd_function_name="test" + setup_libs "preexec" + + export precmd_functions=() + assert_equal "${precmd_functions[*]}" "" + + run safe_append_prompt_command "$test_precmd_function_name" + assert_success + + safe_append_prompt_command "$test_precmd_function_name" + assert_equal "${precmd_functions[*]}" "$test_precmd_function_name" +} + +@test "lib preexec: safe_append_preexec()" { + test_preexec_function_name="test" + setup_libs "preexec" + + export preexec_functions=() + assert_equal "${preexec_functions[*]}" "" + + run safe_append_preexec "$test_preexec_function_name" + assert_success + + safe_append_preexec "$test_preexec_function_name" + assert_equal "${preexec_functions[*]}" "$test_preexec_function_name" +} diff --git a/test/lib/search.bats b/test/lib/search.bats index 2081abab..e28922f4 100644 --- a/test/lib/search.bats +++ b/test/lib/search.bats @@ -1,75 +1,55 @@ -#!/usr/bin/env bats +# shellcheck shell=bats -load ../test_helper -load "${BASH_IT}/vendor/github.com/erichs/composure/composure.sh" -load ../../lib/log -load ../../lib/helpers -load ../../lib/utilities -load ../../lib/search +load "${MAIN_BASH_IT_DIR?}/test/test_helper.bash" -cite _about _param _example _group _author _version - -load ../../plugins/available/base.plugin -load ../../aliases/available/git.aliases -load ../../plugins/available/ruby.plugin -load ../../plugins/available/rails.plugin -load ../../completion/available/bundler.completion -load ../../completion/available/gem.completion -load ../../completion/available/rake.completion - -load ../../lib/helpers - -function local_setup { - setup_test_fixture - - export OLD_PATH="$PATH" - export PATH="/usr/bin:/bin:/usr/sbin" +function local_setup_file() { + setup_libs "search" } -function local_teardown { - export PATH="$OLD_PATH" - unset OLD_PATH +function local_setup() { + # shellcheck disable=SC2034 + BASH_IT_SEARCH_USE_COLOR=false } @test "search: plugin base" { export BASH_IT_SEARCH_USE_COLOR=false run _bash-it-search-component 'plugins' 'base' - assert_line -n 0 ' plugins: base ' + assert_line -n 0 ' plugins: base ' } @test "search: git" { run _bash-it-search 'git' --no-color - assert_line -n 0 ' aliases: git gitsvn ' + assert_line -n 0 ' aliases: git gitsvn ' assert_line -n 1 -p ' plugins:' for plugin in "autojump" "git" "gitstatus" "git-subrepo" "jgitflow" "jump" do echo $plugin assert_line -n 1 -p $plugin done - assert_line -n 2 ' completions: git git_flow git_flow_avh github-cli ' + assert_line -n 2 ' completions: git git_flow git_flow_avh github-cli ' } @test "search: ruby gem bundle rake rails" { run _bash-it-search rails ruby gem bundler rake --no-color - assert_line -n 0 ' aliases: bundler rails ' - assert_line -n 1 ' plugins: chruby chruby-auto rails ruby ' - assert_line -n 2 ' completions: bundler gem rake ' + assert_line -n 0 ' aliases: bundler rails ' + assert_line -n 1 ' plugins: chruby chruby-auto rails ruby ' + assert_line -n 2 ' completions: bundler gem rake ' } @test "search: rails ruby gem bundler rake -chruby" { run _bash-it-search rails ruby gem bundler rake -chruby --no-color - assert_line -n 0 ' aliases: bundler rails ' - assert_line -n 1 ' plugins: rails ruby ' - assert_line -n 2 ' completions: bundler gem rake ' + assert_line -n 0 ' aliases: bundler rails ' + assert_line -n 1 ' plugins: rails ruby ' + assert_line -n 2 ' completions: bundler gem rake ' } @test "search: @git" { run _bash-it-search '@git' --no-color - assert_line -n 0 ' aliases: git ' - assert_line -n 1 ' plugins: git ' - assert_line -n 2 ' completions: git ' + assert_line -n 0 ' aliases: git ' + assert_line -n 1 ' plugins: git ' + assert_line -n 2 ' completions: git ' } @test "search: @git --enable / --disable" { @@ -82,7 +62,7 @@ function local_teardown { run _bash-it-search '@git' --disable --no-color run _bash-it-search '@git' --no-color - assert_line -n 0 ' aliases: git ' - assert_line -n 0 ' aliases: git ' - assert_line -n 2 ' completions: git ' + assert_line -n 0 ' aliases: git ' + assert_line -n 1 ' plugins: git ' + assert_line -n 2 ' completions: git ' } diff --git a/test/lib/utilities.bats b/test/lib/utilities.bats index 4907a763..78913870 100644 --- a/test/lib/utilities.bats +++ b/test/lib/utilities.bats @@ -1,93 +1,83 @@ -#!/usr/bin/env bats +# shellcheck shell=bats -load ../test_helper -load "${BASH_IT}/vendor/github.com/erichs/composure/composure.sh" -load ../../lib/helpers -load ../../lib/utilities -load ../../lib/search +load "${MAIN_BASH_IT_DIR?}/test/test_helper.bash" -cite _about _param _example _group _author _version - -function local_setup { - setup_test_fixture -} - -function has_match() { - $(_bash-it-array-contains-element ${@}) && echo "has" "$1" -} - -function item_enabled() { - $(_bash-it-component-item-is-enabled ${@}) && echo "$1" "$2" "is enabled" -} - -function item_disabled() { - $(_bash-it-component-item-is-disabled ${@}) && echo "$1" "$2" "is disabled" +function local_setup_file() { + setup_libs "helpers" } @test "_bash-it-component-item-is-enabled() - for a disabled item" { - run item_enabled aliases svn - assert_line -n 0 '' + run _bash-it-component-item-is-enabled aliases svn + assert_failure } @test "_bash-it-component-item-is-enabled() - for an enabled/disabled item" { run bash-it enable alias svn assert_line -n 0 'svn enabled with priority 150.' - run item_enabled alias svn - assert_line -n 0 'alias svn is enabled' + run _bash-it-component-item-is-enabled alias svn + assert_success + run _bash-it-component-item-is-disabled alias svn + assert_failure run bash-it disable alias svn assert_line -n 0 'svn disabled.' - run item_enabled alias svn - assert_line -n 0 '' + run _bash-it-component-item-is-enabled alias svn + assert_failure + run _bash-it-component-item-is-disabled alias svn + assert_success } @test "_bash-it-component-item-is-disabled() - for a disabled item" { - run item_disabled alias svn - assert_line -n 0 'alias svn is disabled' + run _bash-it-component-item-is-disabled alias svn + assert_success } @test "_bash-it-component-item-is-disabled() - for an enabled/disabled item" { run bash-it enable alias svn assert_line -n 0 'svn enabled with priority 150.' - run item_disabled alias svn - assert_line -n 0 '' + run _bash-it-component-item-is-disabled alias svn + assert_failure + run _bash-it-component-item-is-enabled alias svn + assert_success run bash-it disable alias svn assert_line -n 0 'svn disabled.' - run item_disabled alias svn - assert_line -n 0 'alias svn is disabled' + run _bash-it-component-item-is-disabled alias svn + assert_success + run _bash-it-component-item-is-enabled alias svn + assert_failure } @test "_bash-it-array-contains-element() - when match is found, and is the first" { declare -a fruits=(apple pear orange mandarin) - run has_match apple "${fruits[@]}" - assert_line -n 0 'has apple' + run _bash-it-array-contains-element apple "${fruits[@]}" + assert_success } @test "_bash-it-array-contains-element() - when match is found, and is the last" { declare -a fruits=(apple pear orange mandarin) - run has_match mandarin "${fruits[@]}" - assert_line -n 0 'has mandarin' + run _bash-it-array-contains-element mandarin "${fruits[@]}" + assert_success } @test "_bash-it-array-contains-element() - when match is found, and is in the middle" { declare -a fruits=(apple pear orange mandarin) - run has_match pear "${fruits[@]}" - assert_line -n 0 'has pear' + run _bash-it-array-contains-element pear "${fruits[@]}" + assert_success } @test "_bash-it-array-contains-element() - when match is found, and it has spaces" { declare -a fruits=(apple pear orange mandarin "yellow watermelon") - run has_match "yellow watermelon" "${fruits[@]}" - assert_line -n 0 'has yellow watermelon' + run _bash-it-array-contains-element "yellow watermelon" "${fruits[@]}" + assert_success } @test "_bash-it-array-contains-element() - when match is not found" { declare -a fruits=(apple pear orange mandarin) - run has_match xyz "${fruits[@]}" - assert_line -n 0 '' + run _bash-it-array-contains-element xyz "${fruits[@]}" + assert_failure } diff --git a/test/plugins/base.plugin.bats b/test/plugins/base.plugin.bats old mode 100755 new mode 100644 index 7daf2619..6022451a --- a/test/plugins/base.plugin.bats +++ b/test/plugins/base.plugin.bats @@ -1,14 +1,13 @@ -#!/usr/bin/env bats +# shellcheck shell=bats -load ../test_helper -load "${BASH_IT}/vendor/github.com/erichs/composure/composure.sh" -load ../../plugins/available/base.plugin +load "${MAIN_BASH_IT_DIR?}/test/test_helper.bash" + +function local_setup_file() { + setup_libs "helpers" + load "${BASH_IT?}/plugins/available/base.plugin.bash" +} @test 'plugins base: ips()' { - if [[ $CI ]]; then - skip 'ifconfig probably requires sudo on TravisCI' - fi - declare -r localhost='127.0.0.1' run ips assert_success @@ -19,11 +18,11 @@ load ../../plugins/available/base.plugin run myip assert_success declare -r mask_ip=$(echo $output | tr -s '[0-9]' '?') - [[ $mask_ip == 'Your public IP is: ?.?.?.?' ]] + [[ $mask_ip == 'Your public IP is:'*'?.?.?.?'* ]] } @test 'plugins base: pickfrom()' { - stub_file="${BASH_IT_ROOT}/stub_file" + stub_file="${BATS_TEST_TMPDIR}/stub_file" printf "l1\nl2\nl3" > $stub_file run pickfrom $stub_file assert_success @@ -31,28 +30,30 @@ load ../../plugins/available/base.plugin } @test 'plugins base: mkcd()' { - cd "${BASH_IT_ROOT}" + cd "${BATS_TEST_TMPDIR}" declare -r dir_name="-dir_with_dash" # Make sure that the directory does not exist prior to the test - rm -rf "${BASH_IT_ROOT}/${dir_name}" + rm -rf "${BATS_TEST_TMPDIR}/${dir_name}" + + run mkcd "${dir_name}" + assert_success + assert_dir_exist "${BATS_TEST_TMPDIR}/${dir_name}" mkcd "${dir_name}" - assert_success - assert_dir_exist "${BASH_IT_ROOT}/${dir_name}" - assert_equal $(pwd) "${BASH_IT_ROOT}/${dir_name}" + assert_equal "${PWD}" "${BATS_TEST_TMPDIR//\/\///}/${dir_name}" } @test 'plugins base: lsgrep()' { - for i in 1 2 3; do mkdir -p "${BASH_IT_TEST_DIR}/${i}"; done - cd $BASH_IT_TEST_DIR + for i in 1 2 3; do mkdir -p "${BASH_IT}/${i}"; done + cd $BASH_IT run lsgrep 2 assert_success assert_equal $output 2 } @test 'plugins base: buf()' { - declare -r file="${BASH_IT_ROOT}/file" + declare -r file="${BATS_TEST_TMPDIR}/file" touch $file # Take one timestamp before running the `buf` function diff --git a/test/plugins/battery.plugin.bats b/test/plugins/battery.plugin.bats index 9af49697..49199ef2 100644 --- a/test/plugins/battery.plugin.bats +++ b/test/plugins/battery.plugin.bats @@ -1,11 +1,11 @@ -#!/usr/bin/env bats +# shellcheck shell=bats -load ../test_helper -load "${BASH_IT}/vendor/github.com/erichs/composure/composure.sh" +load "${MAIN_BASH_IT_DIR?}/test/test_helper.bash" -cite _about _param _example _group _author _version - -load ../../plugins/available/battery.plugin +function local_setup_file() { + setup_libs "helpers" + load "${BASH_IT?}/plugins/available/battery.plugin.bash" +} # Sets up the `_command_exists` function so that it only responds `true` if called with # the name of the function that was passed in as an argument to `setup_command_exists`. @@ -195,11 +195,24 @@ function setup_acpi { # Creates a `upower` function that simulates output like the real `upower` command. # The passed in parameter is used for the remaining battery percentage. function setup_upower { - percent="$1" + percent="$1" + BAT0="/org/freedesktop/UPower/devices/battery_BAT$RANDOM" - function upower { - printf "voltage: 12.191 V\n time to full: 57.3 minutes\n percentage: %s\n capacity: 84.6964" "${percent}" - } + function upower { + case $1 in + '-e'|'--enumerate') + printf '%s\n' "$BAT0" "/org/freedesktop/UPower/devices/mouse_hid_${RANDOM}_battery" + ;; + '-i'|'--show-info') + if [[ $2 == "$BAT0" ]] + then + printf "voltage: 12.191 V\n time to full: 57.3 minutes\n percentage: %s\n capacity: 84.6964" "${percent}" + else + false + fi + ;; + esac + } } @test 'plugins battery: battery-percentage with upower, 100%' { diff --git a/test/plugins/cmd-returned-notify.plugin.bats b/test/plugins/cmd-returned-notify.plugin.bats index f201e465..04edad95 100644 --- a/test/plugins/cmd-returned-notify.plugin.bats +++ b/test/plugins/cmd-returned-notify.plugin.bats @@ -1,47 +1,41 @@ -#!/usr/bin/env bats +# shellcheck shell=bats -load ../test_helper -load ../../lib/helpers -load "${BASH_IT}/vendor/github.com/erichs/composure/composure.sh" +load "${MAIN_BASH_IT_DIR?}/test/test_helper.bash" -load ../../plugins/available/cmd-returned-notify.plugin +function local_setup_file() { + setup_libs "command_duration" + load "${BASH_IT?}/plugins/available/cmd-returned-notify.plugin.bash" +} @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' + export NOTIFY_IF_COMMAND_RETURNS_AFTER=0 + export COMMAND_DURATION_START_SECONDS="${EPOCHREALTIME:-$SECONDS}" + 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 $'' + export NOTIFY_IF_COMMAND_RETURNS_AFTER=10 + export COMMAND_DURATION_START_SECONDS="${EPOCHREALTIME:-$SECONDS}" + 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 "lib command_duration: preexec no output" { + export COMMAND_DURATION_START_SECONDS= + run _command_duration_pre_exec + 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}" +@test "lib command_duration: preexec set COMMAND_DURATION_START_SECONDS" { + export COMMAND_DURATION_START_SECONDS= + assert_equal "${COMMAND_DURATION_START_SECONDS}" "" + NOW="${EPOCHREALTIME:-$SECONDS}" + _command_duration_pre_exec + # We need to make sure to account for nanoseconds... + assert_equal "${COMMAND_DURATION_START_SECONDS%.*}" "${NOW%.*}" } diff --git a/test/plugins/go.plugin.bats b/test/plugins/go.plugin.bats index c53f359f..ebb9cd88 100644 --- a/test/plugins/go.plugin.bats +++ b/test/plugins/go.plugin.bats @@ -1,54 +1,74 @@ -#!/usr/bin/env bats +# shellcheck shell=bats -load ../test_helper -load ../../lib/helpers -load "${BASH_IT}/vendor/github.com/erichs/composure/composure.sh" +load "${MAIN_BASH_IT_DIR?}/test/test_helper.bash" + +function local_setup_file() { + setup_libs "helpers" +} + +function setup_go_path() +{ + local go_path="$1" + + # Make sure that the requested GO folder is available + assert_dir_exist "$go_path/bin" + + # Make sure that the requested GO folder is on the path + export GOPATH="$go_path:${GOPATH:-}" +} + +# We test `go version` in each test to account for users with goenv and no system go. @test 'ensure _bash-it-gopath-pathmunge is defined' { - { [[ $CI ]] || _command_exists go; } || skip 'golang not found' + { _command_exists go && go version &>/dev/null; } || skip 'golang not found' load ../../plugins/available/go.plugin run type -t _bash-it-gopath-pathmunge assert_line 'function' } @test 'plugins go: single entry in GOPATH' { - { [[ $CI ]] || _command_exists go; } || skip 'golang not found' - export GOPATH="/foo" + { _command_exists go && go version &>/dev/null; } || skip 'golang not found' + setup_go_path "$BASH_IT/test/fixtures/go/gopath" load ../../plugins/available/go.plugin - assert_equal "$(cut -d':' -f1 <<<$PATH)" "/foo/bin" + assert_equal "$(cut -d':' -f1 <<<$PATH)" "$BASH_IT/test/fixtures/go/gopath/bin" } @test 'plugins go: single entry in GOPATH, with space' { - { [[ $CI ]] || _command_exists go; } || skip 'golang not found' - export GOPATH="/foo bar" + { _command_exists go && go version &>/dev/null; } || skip 'golang not found' + setup_go_path "$BASH_IT/test/fixtures/go/go path" load ../../plugins/available/go.plugin - assert_equal "$(cut -d':' -f1 <<<$PATH)" "/foo bar/bin" + assert_equal "$(cut -d':' -f1 <<<$PATH)" "$BASH_IT/test/fixtures/go/go path/bin" } @test 'plugins go: single entry in GOPATH, with escaped space' { - { [[ $CI ]] || _command_exists go; } || skip 'golang not found' - export GOPATH="/foo\ bar" + skip 'huh?' + { _command_exists go && go version &>/dev/null; } || skip 'golang not found' + setup_go_path "$BASH_IT/test/fixtures/go/go\ path" load ../../plugins/available/go.plugin - assert_equal "$(cut -d':' -f1 <<<$PATH)" "/foo\ bar/bin" + assert_equal "$(cut -d':' -f1 <<<$PATH)" "$BASH_IT/test/fixtures/go/go\ path/bin" } @test 'plugins go: multiple entries in GOPATH' { - { [[ $CI ]] || _command_exists go; } || skip 'golang not found' - export GOPATH="/foo:/bar" + { _command_exists go && go version &>/dev/null; } || skip 'golang not found' + setup_go_path "$BASH_IT/test/fixtures/go/gopath" + setup_go_path "$BASH_IT/test/fixtures/go/gopath2" load ../../plugins/available/go.plugin - assert_equal "$(cut -d':' -f1,2 <<<$PATH)" "/foo/bin:/bar/bin" + assert_equal "$(cut -d':' -f1,2 <<<$PATH)" "$BASH_IT/test/fixtures/go/gopath2/bin:$BASH_IT/test/fixtures/go/gopath/bin" } @test 'plugins go: multiple entries in GOPATH, with space' { - { [[ $CI ]] || _command_exists go; } || skip 'golang not found' - export GOPATH="/foo:/foo bar" + { _command_exists go && go version &>/dev/null; } || skip 'golang not found' + setup_go_path "$BASH_IT/test/fixtures/go/gopath" + setup_go_path "$BASH_IT/test/fixtures/go/go path" load ../../plugins/available/go.plugin - assert_equal "$(cut -d':' -f1,2 <<<$PATH)" "/foo/bin:/foo bar/bin" + assert_equal "$(cut -d':' -f1,2 <<<$PATH)" "$BASH_IT/test/fixtures/go/go path/bin:$BASH_IT/test/fixtures/go/gopath/bin" } @test 'plugins go: multiple entries in GOPATH, with escaped space' { - { [[ $CI ]] || _command_exists go; } || skip 'golang not found' - export GOPATH="/foo:/foo\ bar" + skip 'huh?' + { _command_exists go && go version &>/dev/null; } || skip 'golang not found' + setup_go_path "$BASH_IT/test/fixtures/go/gopath" + setup_go_path "$BASH_IT/test/fixtures/go/go path" load ../../plugins/available/go.plugin - assert_equal "$(cut -d':' -f1,2 <<<$PATH)" "/foo/bin:/foo\ bar/bin" + assert_equal "$(cut -d':' -f1,2 <<<$PATH)" "$BASH_IT/test/fixtures/go/go\ path/bin:$BASH_IT/test/fixtures/go/gopath/bin" } diff --git a/test/plugins/ruby.plugin.bats b/test/plugins/ruby.plugin.bats old mode 100755 new mode 100644 index 7a719020..7bfc6455 --- a/test/plugins/ruby.plugin.bats +++ b/test/plugins/ruby.plugin.bats @@ -1,34 +1,31 @@ -#!/usr/bin/env bats +# shellcheck shell=bats -load ../test_helper -load ../../lib/helpers -load "${BASH_IT}/vendor/github.com/erichs/composure/composure.sh" -load ../../plugins/available/ruby.plugin +load "${MAIN_BASH_IT_DIR?}/test/test_helper.bash" -function local_setup { - setup_test_fixture - - export OLD_PATH="$PATH" - export PATH="/usr/bin:/bin:/usr/sbin" -} - -function local_teardown { - export PATH="$OLD_PATH" - unset OLD_PATH +function local_setup_file() { + setup_libs "helpers" } @test "plugins ruby: remove_gem is defined" { + run load "${BASH_IT?}/plugins/available/ruby.plugin.bash" + assert_success + load "${BASH_IT?}/plugins/available/ruby.plugin.bash" + run type remove_gem assert_line -n 1 "remove_gem () " } @test "plugins ruby: PATH includes ~/.gem/ruby/bin" { - if ! which ruby >/dev/null; then + if ! type ruby >/dev/null; then skip 'ruby not installed' fi - load ../../plugins/available/ruby.plugin + mkdir -p "$(ruby -e 'print Gem.user_dir')/bin" - local last_path_entry=$(echo $PATH | tr ":" "\n" | tail -1) - [[ "${last_path_entry}" == "${HOME}"/.gem/ruby/*/bin ]] + run load "${BASH_IT?}/plugins/available/ruby.plugin.bash" + assert_success + load "${BASH_IT?}/plugins/available/ruby.plugin.bash" + + local last_path_entry="$(tail -1 <<<"${PATH//:/$'\n'}")" + [[ "${last_path_entry}" == "$(ruby -e 'print Gem.user_dir')/bin" ]] } diff --git a/test/plugins/xterm.plugin.bats b/test/plugins/xterm.plugin.bats index 123a91f0..4cb1ffda 100644 --- a/test/plugins/xterm.plugin.bats +++ b/test/plugins/xterm.plugin.bats @@ -1,50 +1,38 @@ -#!/usr/bin/env bats +# shellcheck shell=bats -load ../test_helper -load ../../lib/helpers -load "${BASH_IT}/vendor/github.com/erichs/composure/composure.sh" +load "${MAIN_BASH_IT_DIR?}/test/test_helper.bash" -load ../../plugins/available/xterm.plugin - -function local_setup { - setup_test_fixture - - # Copy the test fixture to the Bash-it folder - if _command_exists 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 +function local_setup_file() { + setup_libs "helpers" + load "${BASH_IT?}/plugins/available/xterm.plugin.bash" } @test "plugins xterm: shorten command output" { export SHORT_TERM_LINE=true - run _short-command ${BASH_IT}/test/fixtures/plugin/xterm/files/* + run _short-command "${BASH_IT}/test/fixtures/plugin/xterm/files"/* assert_success - assert_output ${BASH_IT}/test/fixtures/plugin/xterm/files/arg0 + 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/* + run _short-command "${BASH_IT}/test/fixtures/plugin/xterm/files"/* assert_success - assert_output "$(echo ${BASH_IT}/test/fixtures/plugin/xterm/files/*)" + 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)" + assert_output "$(basename "${PWD}")" } @test "plugins xterm: full dirname output" { export SHORT_TERM_LINE=false run _short-dirname assert_success - assert_output $PWD + assert_output "${PWD}" } @test "plugins xterm: set xterm title" { diff --git a/test/run b/test/run index 88202916..78467366 100755 --- a/test/run +++ b/test/run @@ -2,18 +2,23 @@ test_directory="$(cd "$(dirname "$0")" && pwd)" bats_executable="${test_directory}/../test_lib/bats-core/bin/bats" +# Locate ourselves for easy reference. +export MAIN_BASH_IT_DIR="${test_directory%/*}" +export MAIN_BASH_IT_GITDIR="${MAIN_BASH_IT_DIR}/.git" + +# Make sure BATS is available: git submodule init && git submodule update -if [ -z "${BASH_IT}" ]; then - declare BASH_IT - BASH_IT=$(cd ${test_directory} && dirname "$(pwd)") - export BASH_IT +# Warn user that tests run from the current git HEAD +if ! git diff --quiet; then + echo "${BASH_SOURCE##*/}: your worktree is dirty; uncommitted changes will *not* be tested!" fi -if [ -z "$1" ]; then - test_dirs=( ${test_directory}/{bash_it,completion,install,lib,plugins,themes} ) +# Which tests do we run? +if [[ $# -eq '0' ]]; then + test_dirs=("${test_directory}"/{bash_it,completion,install,lib,plugins,themes}) else - test_dirs=( "$1" ) + test_dirs=("$@") fi # Make sure that the `parallel` command is installed, @@ -21,28 +26,26 @@ fi # If that is the case, try to guess the number of CPU cores, # so we can run `bats` in parallel processing mode, which is a lot faster. if command -v parallel &> /dev/null \ - && parallel -V &> /dev/null \ - && { parallel -V 2> /dev/null | grep -q '^GNU\>'; } -then - # Expect to run at least on a dual-core CPU; slightly degraded performance - # shouldn't matter otherwise. - declare -i -r test_jobs_default=2 - declare -i -r test_jobs_effective="$( - if [ "${TEST_JOBS:-detect}" = "detect" ] \ - && command -v nproc &> /dev/null - then - nproc - elif [ -n "${TEST_JOBS}" ] \ - && [ "${TEST_JOBS}" != "detect" ] - then - echo "${TEST_JOBS}" - else - echo ${test_jobs_default} - fi - )" - exec "$bats_executable" ${CI:+--tap} --jobs ${test_jobs_effective} \ - "${test_dirs[@]}" + && parallel -V &> /dev/null \ + && { parallel -V 2> /dev/null | grep -q '^GNU\>'; }; then + # Expect to run at least on a dual-core CPU; slightly degraded performance + # shouldn't matter otherwise. + declare -i -r test_jobs_default=2 + # shellcheck disable=SC2155 + declare -i -r test_jobs_effective="$( + if [[ "${TEST_JOBS:-detect}" = "detect" ]] \ + && command -v nproc &> /dev/null; then + nproc + elif [[ -n "${TEST_JOBS}" ]] \ + && [[ "${TEST_JOBS}" != "detect" ]]; then + echo "${TEST_JOBS}" + else + echo "${test_jobs_default}" + fi + )" + exec "$bats_executable" "${CI:+--tap}" --jobs "${test_jobs_effective}" \ + --no-parallelize-within-files "${test_dirs[@]}" else - # Run `bats` in single-threaded mode. - exec "$bats_executable" ${CI:+--tap} "${test_dirs[@]}" + # Run `bats` in single-threaded mode. + exec "$bats_executable" "${CI:+--tap}" "${test_dirs[@]}" fi diff --git a/test/test_helper.bash b/test/test_helper.bash index 8d92e89c..bffb59ed 100644 --- a/test/test_helper.bash +++ b/test/test_helper.bash @@ -1,104 +1,117 @@ -#!/usr/bin/env bats -load "${BASH_IT}/vendor/github.com/erichs/composure/composure.sh" +# shellcheck shell=bash -unset BASH_IT_THEME -unset GIT_HOSTING -unset NGINX_PATH -unset IRC_CLIENT -unset TODO -unset SCM_CHECK -unset BASH_IT_AUTOMATIC_RELOAD_AFTER_CONFIG_CHANGE - -export TEST_MAIN_DIR="${BATS_TEST_DIRNAME}/.." -export TEST_DEPS_DIR="${TEST_DEPS_DIR-${TEST_MAIN_DIR}/../test_lib}" - -# be independent of git's system configuration -export GIT_CONFIG_NOSYSTEM - -load "${TEST_DEPS_DIR}/bats-support/load.bash" -load "${TEST_DEPS_DIR}/bats-assert/load.bash" -load "${TEST_DEPS_DIR}/bats-file/load.bash" - -# support 'plumbing' metadata -cite _about _param _example _group _author _version - -local_setup() { - true +function setup_file() { + common_setup_file } -local_teardown() { - true -} +function common_setup_file() { + # export *everything* to subshells, needed to support tests + set -a -# This function sets up a local test fixture, i.e. a completely -# fresh and isolated Bash-it directory. This is done to avoid -# messing with your own Bash-it source directory. -# If you need this, call it in your .bats file's `local_setup` function. -setup_test_fixture() { - mkdir -p "$BASH_IT" - lib_directory="$(cd "$(dirname "$0")" && pwd)" - local src_topdir="$lib_directory/../../../.." + # Locate ourselves for easy reference. + TEST_MAIN_DIR="${MAIN_BASH_IT_DIR:-${BATS_TEST_DIRNAME?}/../..}/test" + TEST_DEPS_DIR="${MAIN_BASH_IT_DIR:-${TEST_MAIN_DIR}/..}/test_lib" - if command -v rsync &> /dev/null; then - # Use rsync to copy Bash-it to the temp folder - rsync -qavrKL -d --delete-excluded --exclude=.git --exclude=enabled "$src_topdir" "$BASH_IT" - else - rm -rf "$BASH_IT" - mkdir -p "$BASH_IT" + # Load the BATS modules we use: + load "${TEST_DEPS_DIR}/bats-support/load.bash" + load "${TEST_DEPS_DIR}/bats-assert/load.bash" + load "${TEST_DEPS_DIR}/bats-file/load.bash" - find "$src_topdir" \ - -mindepth 1 -maxdepth 1 \ - -not -name .git \ - -exec cp -r {} "$BASH_IT" \; - fi - - rm -rf "$BASH_IT"/enabled - rm -rf "$BASH_IT"/aliases/enabled - rm -rf "$BASH_IT"/completion/enabled - rm -rf "$BASH_IT"/plugins/enabled - - mkdir -p "$BASH_IT"/enabled - mkdir -p "$BASH_IT"/aliases/enabled - mkdir -p "$BASH_IT"/completion/enabled - mkdir -p "$BASH_IT"/plugins/enabled - - # Some tests use the BASH_IT_TEST_HOME variable, e.g. install/uninstall - export BASH_IT_TEST_HOME="$TEST_TEMP_DIR" -} - -setup() { - # The `temp_make` function from "bats-file" requires the tralston/bats-file fork, - # since the original ztombol/bats-file's `temp_make` does not work on macOS. - TEST_TEMP_DIR="$(temp_make --prefix 'bash-it-test-')" - export TEST_TEMP_DIR - - export BASH_IT_TEST_DIR="${TEST_TEMP_DIR}/.bash_it" - - export BASH_IT_ROOT="${BASH_IT_TEST_DIR}/root" - export BASH_IT=$BASH_IT_TEST_DIR - - mkdir -p -- "${BASH_IT_ROOT}" + # shellcheck disable=SC2034 # Clear any inherited environment: + XDG_DUMMY="" BASH_IT_DUMMY="" # avoid possible invalid reference: + unset "${!XDG_@}" "${!BASH_IT@}" # unset all BASH_IT* and XDG_* variables + unset GIT_HOSTING NGINX_PATH IRC_CLIENT TODO SCM_CHECK # Some tools, e.g. `git` use configuration files from the $HOME directory, - # which interferes with our tests. The only way to keep `git` from doing this - # seems to set HOME explicitly to a separate location. + # which interferes with our tests. The only way to keep `git` from doing + # this seems to set HOME explicitly to a separate location. # Refer to https://git-scm.com/docs/git-config#FILES. - unset XDG_CONFIG_HOME - export HOME="${TEST_TEMP_DIR}" + readonly HOME="${BATS_SUITE_TMPDIR?}" mkdir -p "${HOME}" # For `git` tests to run well, user name and email need to be set. # Refer to https://git-scm.com/docs/git-commit#_commit_information. # This goes to the test-specific config, due to the $HOME overridden above. - git config --global user.name "John Doe" - git config --global user.email "johndoe@example.com" + git config --global user.name "Bash It BATS Runner" + git config --global user.email "bats@bash.it" + git config --global advice.detachedHead false + git config --global init.defaultBranch "master" + # Locate the temporary folder, avoid double-slash. + BASH_IT="${BATS_FILE_TMPDIR//\/\///}/.bash_it" + + # This sets up a local test fixture, i.e. a completely fresh and isolated Bash-it directory. This is done to avoid messing with your own Bash-it source directory. + git --git-dir="${MAIN_BASH_IT_GITDIR?}" worktree add -d "${BASH_IT}" + + load "${BASH_IT?}/vendor/github.com/erichs/composure/composure.sh" + # support 'plumbing' metadata + cite _about _param _example _group _author _version + cite about-alias about-plugin about-completion + + # Run any local test setup + local_setup_file + set +a # not needed, but symetiric! +} + +# Load standard _Bash It_ libraries +function setup_libs() { + local lib + # Use a loop to allow convenient short-circuiting for some test files + for lib in "log" "utilities" "helpers" "search" "preexec" "colors" "command_duration"; do + load "${BASH_IT?}/lib/${lib}.bash" || return + # shellcheck disable=SC2015 # short-circuit if we've reached the requested library + [[ "${lib}" == "${1:-}" ]] && return 0 || true + done + return 0 +} + +function local_setup_file() { + true +} + +function local_setup() { + true +} + +function local_teardown() { + true +} + +function clean_test_fixture() { + rm -rf "${BASH_IT_CONFIG?}/enabled" + rm -rf "${BASH_IT_CONFIG?}/aliases/enabled" + rm -rf "${BASH_IT_CONFIG?}/completion/enabled" + rm -rf "${BASH_IT_CONFIG?}/plugins/enabled" + + rm -rf "${BASH_IT_CONFIG?}/tmp/cache" + rm -rf "${BASH_IT_CONFIG?}/profiles"/test*.bash_it +} + +function setup_test_fixture() { + mkdir -p "${BASH_IT_CONFIG?}/enabled" + mkdir -p "${BASH_IT_CONFIG?}/aliases/enabled" + mkdir -p "${BASH_IT_CONFIG?}/completion/enabled" + mkdir -p "${BASH_IT_CONFIG?}/plugins/enabled" +} + +function setup() { + # be independent of git's system configuration + export GIT_CONFIG_NOSYSTEM + # Locate the temporary folder: + BASH_IT_CONFIG="${BASH_IT?}" #"${BATS_TEST_TMPDIR//\/\///}" + export XDG_CACHE_HOME="${BATS_TEST_TMPDIR?}" + + setup_test_fixture local_setup } -teardown() { +function teardown() { + unset GIT_CONFIG_NOSYSTEM local_teardown - - rm -rf "${BASH_IT_TEST_DIR}" - temp_del "${TEST_TEMP_DIR}" + clean_test_fixture +} + +function teardown_file() { + # This only serves to clean metadata from the real git repo. + git --git-dir="${MAIN_BASH_IT_GITDIR?}" worktree remove -f "${BASH_IT?}" } diff --git a/test/themes/base.theme.bats b/test/themes/base.theme.bats index dc8213d3..81b08a01 100644 --- a/test/themes/base.theme.bats +++ b/test/themes/base.theme.bats @@ -1,13 +1,11 @@ -#!/usr/bin/env bats +# shellcheck shell=bats -load ../test_helper -load "${BASH_IT}/vendor/github.com/erichs/composure/composure.sh" -load ../../lib/log +load "${MAIN_BASH_IT_DIR?}/test/test_helper.bash" -cite _about _param _example _group _author _version - -load ../../lib/helpers -load ../../themes/base.theme +function local_setup_file() { + setup_libs "colors" #"theme" + load "${BASH_IT?}/themes/base.theme.bash" +} @test 'themes base: battery_percentage should not exist' { run type -a battery_percentage &> /dev/null @@ -15,37 +13,39 @@ load ../../themes/base.theme } @test 'themes base: battery_percentage should exist if battery plugin loaded' { - load ../../plugins/available/battery.plugin + load "${BASH_IT?}/plugins/available/battery.plugin.bash" run type -a battery_percentage &> /dev/null assert_success } @test 'themes base: battery_char should exist' { - run type -a battery_char &> /dev/null + run type -t battery_char assert_success + assert_line "function" run battery_char - assert_success assert_line -n 0 "" - - run type -a battery_char - assert_line " echo -n" } @test 'themes base: battery_char should exist if battery plugin loaded' { unset -f battery_char - load ../../plugins/available/battery.plugin - load ../../themes/base.theme - run type -a battery_char &> /dev/null + load "${BASH_IT?}/plugins/available/battery.plugin.bash" + run type -t battery_percentage assert_success + assert_line "function" + + load "${BASH_IT?}/themes/base.theme.bash" + run type -t battery_char + assert_success + assert_line "function" run battery_char assert_success run type -a battery_char - assert_line ' if [[ "${THEME_BATTERY_PERCENTAGE_CHECK}" = true ]]; then' + assert_output --partial 'THEME_BATTERY_PERCENTAGE_CHECK' } @test 'themes base: battery_charge should exist' { @@ -54,13 +54,13 @@ load ../../themes/base.theme run battery_charge assert_success - assert_line -n 0 "" + assert_output "" } @test 'themes base: battery_charge should exist if battery plugin loaded' { unset -f battery_charge - load ../../plugins/available/battery.plugin - load ../../themes/base.theme + load "${BASH_IT?}/plugins/available/battery.plugin.bash" + load "${BASH_IT?}/themes/base.theme.bash" run type -a battery_charge &> /dev/null assert_success diff --git a/test/themes/base.theme.git.bats b/test/themes/base.theme.git.bats index f2c4b9c2..b2bc7c5a 100644 --- a/test/themes/base.theme.git.bats +++ b/test/themes/base.theme.git.bats @@ -1,14 +1,14 @@ -#!/usr/bin/env bats +# shellcheck shell=bats +# shellcheck disable=SC2034 +# shellcheck disable=SC2016 -load ../test_helper -load "${BASH_IT}/vendor/github.com/erichs/composure/composure.sh" -load ../../lib/log +load "${MAIN_BASH_IT_DIR?}/test/test_helper.bash" -cite _about _param _example _group _author _version - -load ../../lib/helpers -load ../../themes/githelpers.theme -load ../../themes/base.theme +function local_setup_file() { + setup_libs "colors" #"theme" + load "${BASH_IT?}/themes/base.theme.bash" + load "${BASH_IT?}/themes/githelpers.theme.bash" +} add_commit() { local file_name="general-${RANDOM}" diff --git a/test/themes/base.theme.svn.bats b/test/themes/base.theme.svn.bats index f2866c8d..360e8636 100644 --- a/test/themes/base.theme.svn.bats +++ b/test/themes/base.theme.svn.bats @@ -1,33 +1,10 @@ -#!/usr/bin/env bats +# shellcheck shell=bats -load ../test_helper -load "${BASH_IT}/vendor/github.com/erichs/composure/composure.sh" -load ../../lib/log +load "${MAIN_BASH_IT_DIR?}/test/test_helper.bash" -cite _about _param _example _group _author _version - -load ../../lib/helpers -load ../../themes/base.theme - -function local_setup { - setup_test_fixture - - # Copy the test fixture to the Bash-it folder - if command -v rsync &> /dev/null - then - rsync -a "$BASH_IT/test/fixtures/bash_it/" "$BASH_IT/" - else - find "$BASH_IT/test/fixtures/bash_it" \ - -mindepth 1 -maxdepth 1 \ - -exec cp -r {} "$BASH_IT/" \; - fi - - export OLD_PATH="$PATH" -} - -function local_teardown { - export PATH="$OLD_PATH" - unset OLD_PATH +function local_setup_file() { + setup_libs "colors" #"theme" + load "${BASH_IT?}/themes/base.theme.bash" } function setup_repo { @@ -56,8 +33,8 @@ function setup_svn_path { setup_svn_path "$BASH_IT/test/fixtures/svn/working" - # Load the base theme again so that the working SVN script is detected - load ../../themes/base.theme + # Init the base theme again so that the working SVN script is detected + _bash_it_appearance_scm_init scm # Make sure that the SVN command is used @@ -73,8 +50,8 @@ function setup_svn_path { setup_svn_path "$BASH_IT/test/fixtures/svn/working" - # Load the base theme again so that the working SVN script is detected - load ../../themes/base.theme + # init the base theme again so that the working SVN script is detected + _bash_it_appearance_scm_init scm # Make sure that the SVN command is used @@ -89,8 +66,8 @@ function setup_svn_path { setup_svn_path "$BASH_IT/test/fixtures/svn/working" - # Load the base theme again so that the working SVN script is detected - load ../../themes/base.theme + # Init the base theme again so that the working SVN script is detected + _bash_it_appearance_scm_init scm # Make sure that no SVN command is used @@ -103,8 +80,8 @@ function setup_svn_path { setup_svn_path "$BASH_IT/test/fixtures/svn/broken" - # Load the base theme again so that the broken SVN script is detected - load ../../themes/base.theme + # Init the base theme again so that the broken SVN script is detected + _bash_it_appearance_scm_init scm # Make sure that no SVN command is not used @@ -120,8 +97,8 @@ function setup_svn_path { setup_svn_path "$BASH_IT/test/fixtures/svn/broken" - # Load the base theme again so that the broken SVN script is detected - load ../../themes/base.theme + # Init the base theme again so that the broken SVN script is detected + _bash_it_appearance_scm_init scm # Make sure that no SVN command is used diff --git a/test_lib/bats-core b/test_lib/bats-core index 73b8d2f9..99d64eb0 160000 --- a/test_lib/bats-core +++ b/test_lib/bats-core @@ -1 +1 @@ -Subproject commit 73b8d2f95513207b319efe34685553b75c0b214e +Subproject commit 99d64eb017abcd6a766dd0d354e625526da69cb3 diff --git a/themes/90210/90210.theme.bash b/themes/90210/90210.theme.bash index 180764c8..6b94427a 100644 --- a/themes/90210/90210.theme.bash +++ b/themes/90210/90210.theme.bash @@ -1,17 +1,21 @@ # shellcheck shell=bash -SCM_THEME_PROMPT_DIRTY=" ${red}✗" -SCM_THEME_PROMPT_CLEAN=" ${bold_green}✓" -SCM_THEME_PROMPT_PREFIX=" |" -SCM_THEME_PROMPT_SUFFIX="${green}|" +# shellcheck disable=SC2034 # Expected behavior for themes. -GIT_THEME_PROMPT_DIRTY=" ${red}✗" -GIT_THEME_PROMPT_CLEAN=" ${bold_green}✓" -GIT_THEME_PROMPT_PREFIX=" ${green}|" -GIT_THEME_PROMPT_SUFFIX="${green}|" +SCM_THEME_PROMPT_DIRTY=" ${red?}✗" +SCM_THEME_PROMPT_CLEAN=" ${bold_green?}✓" +SCM_THEME_PROMPT_PREFIX=" |" +SCM_THEME_PROMPT_SUFFIX="${green?}|" + +GIT_THEME_PROMPT_DIRTY=" ${red?}✗" +GIT_THEME_PROMPT_CLEAN=" ${bold_green?}✓" +GIT_THEME_PROMPT_PREFIX=" ${green?}|" +GIT_THEME_PROMPT_SUFFIX="${green?}|" # Nicely formatted terminal prompt function prompt_command() { - export PS1="\n${bold_black}[${blue}\@${bold_black}]-${bold_black}[${green}\u${yellow}@${green}\h${bold_black}]-${bold_black}[${purple}\w${bold_black}]-$(scm_prompt_info)\n${reset_color}\$ " + local scm_prompt_info + scm_prompt_info="$(scm_prompt_info)" + PS1="\n${bold_black?}[${blue?}\@${bold_black?}]-${bold_black?}[${green?}\u${yellow?}@${green?}\h${bold_black?}]-${bold_black?}[${purple?}\w${bold_black?}]-${scm_prompt_info?}\n${reset_color?}\$ " } safe_append_prompt_command prompt_command diff --git a/themes/agnoster/agnoster.theme.bash b/themes/agnoster/agnoster.theme.bash index ba3e2c9a..20c184f3 100644 --- a/themes/agnoster/agnoster.theme.bash +++ b/themes/agnoster/agnoster.theme.bash @@ -1,4 +1,5 @@ # shellcheck shell=bash +# shellcheck disable=SC2034 # Expected behavior for themes. # vim: ft=bash ts=2 sw=2 sts=2 # # agnoster's Theme - https://gist.github.com/3712874 @@ -220,7 +221,7 @@ prompt_virtualenv() { # Context: user@hostname (who am I and where am I) prompt_context() { - local user=$(whoami) + local user="${USER:-${LOGNAME:?}}" if [[ $user != "$DEFAULT_USER" || -n $SSH_CLIENT ]]; then prompt_segment black default "$user@\h" diff --git a/themes/atomic/atomic.theme.bash b/themes/atomic/atomic.theme.bash index 6bde6801..c59dbdc9 100644 --- a/themes/atomic/atomic.theme.bash +++ b/themes/atomic/atomic.theme.bash @@ -1,4 +1,5 @@ # shellcheck shell=bash +# shellcheck disable=SC2034 # Expected behavior for themes. # Atomic Bash Prompt for Bash-it # By lfelipe base on the theme brainy of MunifTanjim @@ -27,48 +28,48 @@ Face="\342\230\273" ## Parsers ## ############# -____atomic_top_left_parse() { - ifs_old="${IFS}" - IFS="|" +function ____atomic_top_left_parse() { + local ifs_old="${IFS}" + local IFS="|" read -r -a args <<< "$@" IFS="${ifs_old}" - if [ -n "${args[3]}" ]; then - _TOP_LEFT+="${args[2]}${args[3]}" + if [[ -n "${args[3]:-}" ]]; then + _TOP_LEFT+="${args[2]?}${args[3]?}" fi - _TOP_LEFT+="${args[0]}${args[1]}" - if [ -n "${args[4]}" ]; then - _TOP_LEFT+="${args[2]}${args[4]}" + _TOP_LEFT+="${args[0]?}${args[1]:-}" + if [[ -n "${args[4]:-}" ]]; then + _TOP_LEFT+="${args[2]?}${args[4]?}" fi _TOP_LEFT+="" } -____atomic_top_right_parse() { - ifs_old="${IFS}" - IFS="|" +function ____atomic_top_right_parse() { + local ifs_old="${IFS}" + local IFS="|" read -r -a args <<< "$@" IFS="${ifs_old}" _TOP_RIGHT+=" " - if [ -n "${args[3]}" ]; then - _TOP_RIGHT+="${args[2]}${args[3]}" + if [[ -n "${args[3]:-}" ]]; then + _TOP_RIGHT+="${args[2]?}${args[3]?}" fi - _TOP_RIGHT+="${args[0]}${args[1]}" - if [ -n "${args[4]}" ]; then - _TOP_RIGHT+="${args[2]}${args[4]}" + _TOP_RIGHT+="${args[0]?}${args[1]:-}" + if [[ -n "${args[4]:-}" ]]; then + _TOP_RIGHT+="${args[2]?}${args[4]?}" fi __TOP_RIGHT_LEN=$((__TOP_RIGHT_LEN + ${#args[1]} + ${#args[3]} + ${#args[4]} + 1)) ((__SEG_AT_RIGHT += 1)) } -____atomic_bottom_parse() { - ifs_old="${IFS}" - IFS="|" +function ____atomic_bottom_parse() { + local ifs_old="${IFS}" + local IFS="|" read -r -a args <<< "$@" IFS="${ifs_old}" - _BOTTOM+="${args[0]}${args[1]}" - [ ${#args[1]} -gt 0 ] && _BOTTOM+=" " + _BOTTOM+="${args[0]?}${args[1]?${FUNCNAME[0]}}" + [[ ${#args[1]} -gt 0 ]] && _BOTTOM+=" " } -____atomic_top() { +function ____atomic_top() { _TOP_LEFT="" _TOP_RIGHT="" __TOP_RIGHT_LEN=0 @@ -76,7 +77,7 @@ ____atomic_top() { for seg in ${___ATOMIC_TOP_LEFT}; do info="$(___atomic_prompt_"${seg}")" - [ -n "${info}" ] && ____atomic_top_left_parse "${info}" + [[ -n "${info}" ]] && ____atomic_top_left_parse "${info}" done ___cursor_right="\e[500C" @@ -84,21 +85,21 @@ ____atomic_top() { for seg in ${___ATOMIC_TOP_RIGHT}; do info="$(___atomic_prompt_"${seg}")" - [ -n "${info}" ] && ____atomic_top_right_parse "${info}" + [[ -n "${info}" ]] && ____atomic_top_right_parse "${info}" done - [ $__TOP_RIGHT_LEN -gt 0 ] && __TOP_RIGHT_LEN=$((__TOP_RIGHT_LEN - 0)) + [[ $__TOP_RIGHT_LEN -gt 0 ]] && __TOP_RIGHT_LEN=$((__TOP_RIGHT_LEN - 0)) ___cursor_adjust="\e[${__TOP_RIGHT_LEN}D" _TOP_LEFT+="${___cursor_adjust}" printf "%s%s" "${_TOP_LEFT}" "${_TOP_RIGHT}" } -____atomic_bottom() { +function ____atomic_bottom() { _BOTTOM="" for seg in $___ATOMIC_BOTTOM; do info="$(___atomic_prompt_"${seg}")" - [ -n "${info}" ] && ____atomic_bottom_parse "${info}" + [[ -n "${info}" ]] && ____atomic_bottom_parse "${info}" done printf "\n%s" "${_BOTTOM}" } @@ -107,95 +108,96 @@ ____atomic_bottom() { ## Segments ## ############## -___atomic_prompt_user_info() { - color=$white - box="${normal}${LineA}\$([[ \$? != 0 ]] && echo \"${BIWhite}[${IRed}${SX}${BIWhite}]${normal}${Line}\")${Line}${BIWhite}[|${BIWhite}]${normal}${Line}" - info="${IYellow}\u${IRed}@${IGreen}\h" +function ___atomic_prompt_user_info() { + local color="${white?}" box + local info="${IYellow}\u${IRed}@${IGreen}\h" + box="${normal?}${LineA?}\$([[ \$? != 0 ]] && echo \"${BIWhite?}[${IRed?}${SX?}${BIWhite?}]${normal?}${Line?}\")${Line?}${BIWhite?}[|${BIWhite?}]${normal?}${Line?}" - printf "%s|%s|%s|%s" "${color}" "${info}" "${white}" "${box}" + printf "%s|%s|%s|%s" "${color}" "${info}" "${white?}" "${box}" } -___atomic_prompt_dir() { - color=${IRed} - box="[|]${normal}" - info="\w" - printf "%s|%s|%s|%s" "${color}" "${info}" "${bold_white}" "${box}" +function ___atomic_prompt_dir() { + local color="${IRed?}" + local box="[|]${normal}" + local info="\w" + printf "%s|%s|%s|%s" "${color}" "${info}" "${bold_white?}" "${box}" } -___atomic_prompt_scm() { - [ "${THEME_SHOW_SCM}" != "true" ] && return - color=$bold_green - box="${Line}[${IWhite}$(scm_char)] " +function ___atomic_prompt_scm() { + [[ "${THEME_SHOW_SCM:-}" != "true" ]] && return + local color="${bold_green?}" box info + box="${Line?}[${IWhite?}$(scm_char)] " info="$(scm_prompt_info)" - printf "%s|%s|%s|%s" "${color}" "${info}" "${bold_white}" "${box}" + printf "%s|%s|%s|%s" "${color}" "${info}" "${bold_white?}" "${box}" } -___atomic_prompt_python() { - [ "${THEME_SHOW_PYTHON}" != "true" ] && return - color=$bold_yellow - box="[|]" +function ___atomic_prompt_python() { + [[ "${THEME_SHOW_PYTHON:-}" != "true" ]] && return + local color="${bold_yellow?}" + local box="[|]" info info="$(python_version_prompt)" - printf "%s|%s|%s|%s" "${color}" "${info}" "${bold_blue}" "${box}" + printf "%s|%s|%s|%s" "${color}" "${info}" "${bold_blue?}" "${box}" } -___atomic_prompt_ruby() { - [ "${THEME_SHOW_RUBY}" != "true" ] && return - color=$bold_white - box="[|]" +function ___atomic_prompt_ruby() { + [[ "${THEME_SHOW_RUBY:-}" != "true" ]] && return + local color="${bold_white?}" + local box="[|]" info info="rb-$(ruby_version_prompt)" - printf "%s|%s|%s|%s" "${color}" "${info}" "${bold_red}" "${box}" + printf "%s|%s|%s|%s" "${color}" "${info}" "${bold_red?}" "${box}" } -___atomic_prompt_todo() { - [ "${THEME_SHOW_TODO}" != "true" ] \ - || [ -z "$(which todo.sh)" ] && return - color=$bold_white - box="[|]" +function ___atomic_prompt_todo() { + [[ "${THEME_SHOW_TODO:-}" != "true" || + -z "$(which todo.sh)" ]] && return + local color="${bold_white?}" + local box="[|]" info info="t:$(todo.sh ls | grep -E "TODO: [0-9]+ of ([0-9]+)" | awk '{ print $4 }')" - printf "%s|%s|%s|%s" "${color}" "${info}" "${bold_green}" "${box}" + printf "%s|%s|%s|%s" "${color}" "${info}" "${bold_green?}" "${box}" } -___atomic_prompt_clock() { - [ "${THEME_SHOW_CLOCK}" != "true" ] && return - color=$THEME_CLOCK_COLOR - box="[|]" +function ___atomic_prompt_clock() { + [[ "${THEME_SHOW_CLOCK:-}" != "true" ]] && return + local color="${THEME_CLOCK_COLOR:-}" + local box="[|]" info info="$(date +"${THEME_CLOCK_FORMAT}")" - printf "%s|%s|%s|%s" "${color}" "${info}" "${bold_white}" "${box}" + printf "%s|%s|%s|%s" "${color}" "${info}" "${bold_white?}" "${box}" } -___atomic_prompt_battery() { +function ___atomic_prompt_battery() { + local batp box info ! _command_exists battery_percentage \ - || [ "${THEME_SHOW_BATTERY}" != "true" ] \ - || [ "$(battery_percentage)" = "no" ] && return + || [[ "${THEME_SHOW_BATTERY:-}" != "true" ]] \ + || [[ "$(battery_percentage)" = "no" ]] && return batp=$(battery_percentage) - if [ "$batp" -eq 50 ] || [ "$batp" -gt 50 ]; then - color=$bold_green - elif [ "$batp" -lt 50 ] && [ "$batp" -gt 25 ]; then - color=$bold_yellow - elif [ "$batp" -eq 25 ] || [ "$batp" -lt 25 ]; then - color=$IRed + if [[ "$batp" -eq 50 || "$batp" -gt 50 ]]; then + color="${bold_green?}" + elif [[ "$batp" -lt 50 && "$batp" -gt 25 ]]; then + color="${bold_yellow?}" + elif [[ "$batp" -eq 25 || "$batp" -lt 25 ]]; then + color="${IRed?}" fi box="[|]" ac_adapter_connected && info="+" ac_adapter_disconnected && info="-" info+=$batp - [ "$batp" -eq 100 ] || [ "$batp" -gt 100 ] && info="AC" - printf "%s|%s|%s|%s" "${color}" "${info}" "${bold_white}" "${box}" + [[ "$batp" -eq 100 || "$batp" -gt 100 ]] && info="AC" + printf "%s|%s|%s|%s" "${color}" "${info}" "${bold_white?}" "${box}" } -___atomic_prompt_exitcode() { - [ "${THEME_SHOW_EXITCODE}" != "true" ] && return - color=$bold_purple - [ "$exitcode" -ne 0 ] && printf "%s|%s" "${color}" "${exitcode}" +function ___atomic_prompt_exitcode() { + [[ "${THEME_SHOW_EXITCODE:-}" != "true" ]] && return + local color="${bold_purple?}" + [[ "${exitcode?}" -ne 0 ]] && printf "%s|%s" "${color}" "${exitcode}" } -___atomic_prompt_char() { - color=$white - prompt_char="${__ATOMIC_PROMPT_CHAR_PS1}" - if [ "${THEME_SHOW_SUDO}" == "true" ]; then +function ___atomic_prompt_char() { + local color="${white?}" + local prompt_char="${__ATOMIC_PROMPT_CHAR_PS1?}" + if [[ "${THEME_SHOW_SUDO:-}" == "true" ]]; then if sudo -vn 1> /dev/null 2>&1; then - prompt_char="${__ATOMIC_PROMPT_CHAR_PS1_SUDO}" + prompt_char="${__ATOMIC_PROMPT_CHAR_PS1_SUDO?}" fi fi printf "%s|%s" "${color}" "${prompt_char}" @@ -205,19 +207,17 @@ ___atomic_prompt_char() { ## cli ## ######### -__atomic_show() { - typeset _seg=${1:-} - shift +function __atomic_show() { + local _seg="${1?}" export "THEME_SHOW_${_seg}"=true } -__atomic_hide() { - typeset _seg=${1:-} - shift +function __atomic_hide() { + local _seg="${1?}" export "THEME_SHOW_${_seg}"=false } -_atomic_completion() { +function _atomic_completion() { local cur _action actions segments COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" @@ -237,22 +237,26 @@ _atomic_completion() { return 0 } -atomic() { - typeset action=${1:-} +function atomic() { + local action="${1?}" shift - typeset segs=${*:-} - typeset func - case $action in + local segs=("${@?}") + local func + case "${action}" in show) func=__atomic_show ;; hide) func=__atomic_hide ;; + *) + _log_error "${FUNCNAME[0]}: unknown action '${action}'" + return 1 + ;; esac - for seg in ${segs}; do - seg=$(printf "%s" "${seg}" | tr '[:lower:]' '[:upper:]') - $func "${seg}" + for seg in "${segs[@]}"; do + seg="$(printf "%s" "${seg}" | tr '[:lower:]' '[:upper:]')" + "${func}" "${seg}" done } @@ -262,55 +266,55 @@ complete -F _atomic_completion atomic ## Variables ## ############### -export SCM_THEME_PROMPT_PREFIX="" -export SCM_THEME_PROMPT_SUFFIX="" +SCM_THEME_PROMPT_PREFIX="" +SCM_THEME_PROMPT_SUFFIX="" -export RBENV_THEME_PROMPT_PREFIX="" -export RBENV_THEME_PROMPT_SUFFIX="" -export RBFU_THEME_PROMPT_PREFIX="" -export RBFU_THEME_PROMPT_SUFFIX="" -export RVM_THEME_PROMPT_PREFIX="" -export RVM_THEME_PROMPT_SUFFIX="" +RBENV_THEME_PROMPT_PREFIX="" +RBENV_THEME_PROMPT_SUFFIX="" +RBFU_THEME_PROMPT_PREFIX="" +RBFU_THEME_PROMPT_SUFFIX="" +RVM_THEME_PROMPT_PREFIX="" +RVM_THEME_PROMPT_SUFFIX="" -export SCM_THEME_PROMPT_DIRTY=" ${bold_red}✗${normal}" -export SCM_THEME_PROMPT_CLEAN=" ${bold_green}✓${normal}" +SCM_THEME_PROMPT_DIRTY=" ${bold_red}✗${normal}" +SCM_THEME_PROMPT_CLEAN=" ${bold_green}✓${normal}" -THEME_SHOW_SUDO=${THEME_SHOW_SUDO:-"true"} -THEME_SHOW_SCM=${THEME_SHOW_SCM:-"true"} -THEME_SHOW_RUBY=${THEME_SHOW_RUBY:-"false"} -THEME_SHOW_PYTHON=${THEME_SHOW_PYTHON:-"false"} -THEME_SHOW_CLOCK=${THEME_SHOW_CLOCK:-"true"} -THEME_SHOW_TODO=${THEME_SHOW_TODO:-"false"} -THEME_SHOW_BATTERY=${THEME_SHOW_BATTERY:-"true"} -THEME_SHOW_EXITCODE=${THEME_SHOW_EXITCODE:-"false"} +: "${THEME_SHOW_SUDO:="true"}" +: "${THEME_SHOW_SCM:="true"}" +: "${THEME_SHOW_RUBY:="false"}" +: "${THEME_SHOW_PYTHON:="false"}" +: "${THEME_SHOW_CLOCK:="true"}" +: "${THEME_SHOW_TODO:="false"}" +: "${THEME_SHOW_BATTERY:="true"}" +: "${THEME_SHOW_EXITCODE:="false"}" -THEME_CLOCK_COLOR=${THEME_CLOCK_COLOR:-"${BICyan}"} -THEME_CLOCK_FORMAT=${THEME_CLOCK_FORMAT:-"%a %b %d - %H:%M"} +: "${THEME_CLOCK_COLOR:=${BICyan?}}" +: "${THEME_CLOCK_FORMAT:="%a %b %d - %H:%M"}" -__ATOMIC_PROMPT_CHAR_PS1=${THEME_PROMPT_CHAR_PS1:-"${normal}${LineB}${bold_white}${Circle}"} -__ATOMIC_PROMPT_CHAR_PS2=${THEME_PROMPT_CHAR_PS2:-"${normal}${LineB}${bold_white}${Circle}"} +__ATOMIC_PROMPT_CHAR_PS1=${THEME_PROMPT_CHAR_PS1:-"${normal?}${LineB?}${bold_white?}${Circle?}"} +__ATOMIC_PROMPT_CHAR_PS2=${THEME_PROMPT_CHAR_PS2:-"${normal?}${LineB?}${bold_white?}${Circle?}"} -__ATOMIC_PROMPT_CHAR_PS1_SUDO=${THEME_PROMPT_CHAR_PS1_SUDO:-"${normal}${LineB}${bold_red}${Face}"} -__ATOMIC_PROMPT_CHAR_PS2_SUDO=${THEME_PROMPT_CHAR_PS2_SUDO:-"${normal}${LineB}${bold_red}${Face}"} +__ATOMIC_PROMPT_CHAR_PS1_SUDO=${THEME_PROMPT_CHAR_PS1_SUDO:-"${normal?}${LineB?}${bold_red?}${Face?}"} +__ATOMIC_PROMPT_CHAR_PS2_SUDO=${THEME_PROMPT_CHAR_PS2_SUDO:-"${normal?}${LineB?}${bold_red?}${Face?}"} -___ATOMIC_TOP_LEFT=${___ATOMIC_TOP_LEFT:-"user_info dir scm"} -___ATOMIC_TOP_RIGHT=${___ATOMIC_TOP_RIGHT:-"exitcode python ruby todo clock battery"} -___ATOMIC_BOTTOM=${___ATOMIC_BOTTOM:-"char"} +: "${___ATOMIC_TOP_LEFT:="user_info dir scm"}" +: "${___ATOMIC_TOP_RIGHT:="exitcode python ruby todo clock battery"}" +: "${___ATOMIC_BOTTOM:="char"}" ############ ## Prompt ## ############ -__atomic_ps1() { - printf "%s%s%s" "$(____atomic_top)" "$(____atomic_bottom)" "${normal}" +function __atomic_ps1() { + printf "%s%s%s" "$(____atomic_top)" "$(____atomic_bottom)" "${normal?}" } -__atomic_ps2() { - color=$bold_white - printf "%s%s%s" "${color}" "${__ATOMIC_PROMPT_CHAR_PS2} " "${normal}" +function __atomic_ps2() { + color="${bold_white?}" + printf "%s%s%s" "${color}" "${__ATOMIC_PROMPT_CHAR_PS2?} " "${normal?}" } -_atomic_prompt() { +function _atomic_prompt() { exitcode="$?" PS1="$(__atomic_ps1)" diff --git a/themes/bakke/bakke.theme.bash b/themes/bakke/bakke.theme.bash index 725350a0..a3670b07 100644 --- a/themes/bakke/bakke.theme.bash +++ b/themes/bakke/bakke.theme.bash @@ -1,14 +1,15 @@ # shellcheck shell=bash +# shellcheck disable=SC2034 # Expected behavior for themes. -SCM_THEME_PROMPT_DIRTY=" ${red}✗" -SCM_THEME_PROMPT_CLEAN=" ${bold_green}✓" +SCM_THEME_PROMPT_DIRTY=" ${red?}✗" +SCM_THEME_PROMPT_CLEAN=" ${bold_green?}✓" SCM_THEME_PROMPT_PREFIX=" |" -SCM_THEME_PROMPT_SUFFIX="${green}|" +SCM_THEME_PROMPT_SUFFIX="${green?}|" -GIT_THEME_PROMPT_DIRTY=" ${red}✗" -GIT_THEME_PROMPT_CLEAN=" ${bold_green}✓" -GIT_THEME_PROMPT_PREFIX=" ${green}|" -GIT_THEME_PROMPT_SUFFIX="${green}|" +GIT_THEME_PROMPT_DIRTY=" ${red?}✗" +GIT_THEME_PROMPT_CLEAN=" ${bold_green?}✓" +GIT_THEME_PROMPT_PREFIX=" ${green?}|" +GIT_THEME_PROMPT_SUFFIX="${green?}|" RVM_THEME_PROMPT_PREFIX="|" RVM_THEME_PROMPT_SUFFIX="|" @@ -17,7 +18,10 @@ function prompt_command() { #PS1="${bold_cyan}$(scm_char)${green}$(scm_prompt_info)${purple}$(ruby_version_prompt) ${yellow}\h ${reset_color}in ${green}\w ${reset_color}\n${green}→${reset_color} " #PS1="\n${purple}\h: ${reset_color} ${green}\w\n${bold_cyan}$(scm_char)${green}$(scm_prompt_info) ${green}→${reset_color} " #PS1="\n${cyan}\h: ${reset_color} ${yellow}\w\n${red}$(scm_char)${red}$(scm_prompt_info) ${green}→${reset_color} " - PS1="\n${cyan}\h:$(virtualenv_prompt) ${reset_color} ${yellow}\w ${green}$(scm_prompt_info)\n${reset_color}→ " + local virtualenv_prompt scm_prompt_info + virtualenv_prompt="$(virtualenv_prompt)" + scm_prompt_info="$(scm_prompt_info)" + PS1="\n${cyan?}\h:${virtualenv_prompt} ${reset_color?} ${yellow?}\w ${green?}${scm_prompt_info}\n${reset_color?}→ " } safe_append_prompt_command prompt_command diff --git a/themes/barbuk/barbuk.theme.bash b/themes/barbuk/barbuk.theme.bash index 8e09d4be..b614d148 100644 --- a/themes/barbuk/barbuk.theme.bash +++ b/themes/barbuk/barbuk.theme.bash @@ -1,4 +1,6 @@ # shellcheck shell=bash +# shellcheck disable=SC2034 # Expected behavior for themes. +# shellcheck disable=SC2154 #TODO: fix these all. # Theme custom glyphs SCM_GIT_CHAR_GITLAB=${BARBUK_GITLAB_CHAR:=' '} diff --git a/themes/base.theme.bash b/themes/base.theme.bash index 80763f28..1b170307 100644 --- a/themes/base.theme.bash +++ b/themes/base.theme.bash @@ -1,15 +1,20 @@ # shellcheck shell=bash +# shellcheck disable=SC2034 # Expected behavior for themes. + +# Colors for listing files, using default color scheme. +# To customize color scheme by theme, check out https://geoff.greer.fm/lscolors/ +export CLICOLOR LSCOLORS LS_COLORS CLOCK_CHAR_THEME_PROMPT_PREFIX='' CLOCK_CHAR_THEME_PROMPT_SUFFIX='' CLOCK_THEME_PROMPT_PREFIX='' CLOCK_THEME_PROMPT_SUFFIX='' -THEME_PROMPT_HOST='\H' +THEME_PROMPT_HOST='\h' SCM= -SCM_CHECK=${SCM_CHECK:=true} +: "${SCM_CHECK:=true}" SCM_THEME_PROMPT_DIRTY=' ✗' SCM_THEME_PROMPT_CLEAN=' ✓' @@ -25,17 +30,19 @@ SCM_THEME_CURRENT_USER_SUFFIX='' SCM_THEME_CHAR_PREFIX='' SCM_THEME_CHAR_SUFFIX='' -THEME_BATTERY_PERCENTAGE_CHECK=${THEME_BATTERY_PERCENTAGE_CHECK:=true} +# Define this here so it can be used by all of the themes +: "${THEME_CHECK_SUDO:=false}" +: "${THEME_BATTERY_PERCENTAGE_CHECK:=true}" -SCM_GIT_SHOW_DETAILS=${SCM_GIT_SHOW_DETAILS:=true} -SCM_GIT_SHOW_REMOTE_INFO=${SCM_GIT_SHOW_REMOTE_INFO:=auto} -SCM_GIT_IGNORE_UNTRACKED=${SCM_GIT_IGNORE_UNTRACKED:=false} -SCM_GIT_SHOW_CURRENT_USER=${SCM_GIT_SHOW_CURRENT_USER:=false} -SCM_GIT_SHOW_MINIMAL_INFO=${SCM_GIT_SHOW_MINIMAL_INFO:=false} -SCM_GIT_SHOW_STASH_INFO=${SCM_GIT_SHOW_STASH_INFO:=true} -SCM_GIT_SHOW_COMMIT_COUNT=${SCM_GIT_SHOW_COMMIT_COUNT:=true} -SCM_GIT_USE_GITSTATUS=${SCM_GIT_USE_GITSTATUS:=false} -SCM_GIT_GITSTATUS_RAN=${SCM_GIT_GITSTATUS_RAN:=false} +: "${SCM_GIT_SHOW_DETAILS:=true}" +: "${SCM_GIT_SHOW_REMOTE_INFO:=auto}" +: "${SCM_GIT_IGNORE_UNTRACKED:=false}" +: "${SCM_GIT_SHOW_CURRENT_USER:=false}" +: "${SCM_GIT_SHOW_MINIMAL_INFO:=false}" +: "${SCM_GIT_SHOW_STASH_INFO:=true}" +: "${SCM_GIT_SHOW_COMMIT_COUNT:=true}" +: "${SCM_GIT_USE_GITSTATUS:=false}" +: "${SCM_GIT_GITSTATUS_RAN:=false}" SCM_GIT='git' SCM_GIT_CHAR='±' @@ -70,9 +77,9 @@ NVM_THEME_PROMPT_SUFFIX='|' RVM_THEME_PROMPT_PREFIX=' |' RVM_THEME_PROMPT_SUFFIX='|' -THEME_SHOW_RUBY_PROMPT=${THEME_SHOW_RUBY_PROMPT:=true} +: "${THEME_SHOW_RUBY_PROMPT:=true}" -THEME_SHOW_USER_HOST=${THEME_SHOW_USER_HOST:=false} +: "${THEME_SHOW_USER_HOST:=false}" USER_HOST_THEME_PROMPT_PREFIX='' USER_HOST_THEME_PROMPT_SUFFIX='' @@ -85,147 +92,160 @@ RBENV_THEME_PROMPT_SUFFIX='|' RBFU_THEME_PROMPT_PREFIX=' |' RBFU_THEME_PROMPT_SUFFIX='|' -GIT_EXE=$(which git 2> /dev/null || true) -P4_EXE=$(which p4 2> /dev/null || true) -HG_EXE=$(which hg 2> /dev/null || true) -SVN_EXE=$(which svn 2> /dev/null || true) +: "${GIT_EXE:=${SCM_GIT?}}" +: "${HG_EXE:=${SCM_HG?}}" +: "${SVN_EXE:=${SCM_SVN?}}" +: "${P4_EXE:=${SCM_P4?}}" -# Check for broken SVN exe that is caused by some versions of Xcode. -# See https://github.com/Bash-it/bash-it/issues/1612 for more details. -if [[ -x "$SVN_EXE" ]]; then - if ! "$SVN_EXE" --version > /dev/null 2>&1; then - # Unset the SVN exe variable so that SVN commands are avoided. - SVN_EXE="" +function _bash_it_appearance_scm_init() { + GIT_EXE="$(type -P "${SCM_GIT:-git}" || true)" + HG_EXE="$(type -P "${SCM_HG:-hg}" || true)" + SVN_EXE="$(type -P "${SCM_SVN:-svn}" || true)" + P4_EXE="$(type -P "${SCM_P4:-p4}" || true)" + + # Check for broken SVN exe that is caused by some versions of Xcode. + # See https://github.com/Bash-it/bash-it/issues/1612 for more details. + if [[ -x "${SVN_EXE-}" && -x "${SVN_EXE%/svn}/xcrun" ]]; then + if ! "${SVN_EXE}" --version > /dev/null 2>&1; then + # Unset the SVN exe variable so that SVN commands are avoided. + SVN_EXE="" + fi fi -fi + return 0 +} +_bash_it_library_finalize_hook+=('_bash_it_appearance_scm_init') -function scm { - if [[ "$SCM_CHECK" = false ]]; then - SCM=$SCM_NONE - elif [[ -f .git/HEAD ]] && [[ -x "$GIT_EXE" ]]; then - SCM=$SCM_GIT - elif [[ -x "$GIT_EXE" ]] && [[ -n "$(git rev-parse --is-inside-work-tree 2> /dev/null)" ]]; then - SCM=$SCM_GIT - elif [[ -x "$P4_EXE" ]] && [[ -n "$(p4 set P4CLIENT 2> /dev/null)" ]]; then - SCM=$SCM_P4 - elif [[ -d .hg ]] && [[ -x "$HG_EXE" ]]; then - SCM=$SCM_HG - elif [[ -x "$HG_EXE" ]] && [[ -n "$(hg root 2> /dev/null)" ]]; then - SCM=$SCM_HG - elif [[ -d .svn ]] && [[ -x "$SVN_EXE" ]]; then - SCM=$SCM_SVN - elif [[ -x "$SVN_EXE" ]] && [[ -n "$(svn info --show-item wc-root 2> /dev/null)" ]]; then - SCM=$SCM_SVN +function scm() { + if [[ "${SCM_CHECK:-true}" == "false" ]]; then + SCM="${SCM_NONE-NONE}" + elif [[ -x "${GIT_EXE-}" ]] && _bash-it-find-in-ancestor '.git' > /dev/null; then + SCM="${SCM_GIT?}" + elif [[ -x "${HG_EXE-}" ]] && _bash-it-find-in-ancestor '.hg' > /dev/null; then + SCM="${SCM_HG?}" + elif [[ -x "${SVN_EXE-}" ]] && _bash-it-find-in-ancestor '.svn' > /dev/null; then + SCM="${SCM_SVN?}" + elif [[ -x "${P4_EXE-}" && -n "$(p4 set P4CLIENT 2> /dev/null)" ]]; then + SCM="${SCM_P4?}" else - SCM=$SCM_NONE + SCM="${SCM_NONE-NONE}" fi } -scm_prompt() { - local CHAR=$(scm_char) - local format=${SCM_PROMPT_FORMAT:-'[%s%s]'} +function scm_prompt() { + local format="${SCM_PROMPT_FORMAT-"[%s%s]"}" + local scm_char scm_prompt_info + scm_char="$(scm_char)" + scm_prompt_info="$(scm_prompt_info)" - if [[ $CHAR = "$SCM_NONE_CHAR" ]]; then - return - else + if [[ "${scm_char}" != "${SCM_NONE_CHAR:-}" ]]; then # shellcheck disable=2059 - printf "$format\n" "$CHAR" "$(scm_prompt_info)" + printf "${format}" "${scm_char}" "${scm_prompt_info}" fi } -function scm_prompt_char { - if [[ -z $SCM ]]; then scm; fi - if [[ $SCM == "$SCM_GIT" ]]; then - SCM_CHAR=$SCM_GIT_CHAR - elif [[ $SCM == "$SCM_P4" ]]; then - SCM_CHAR=$SCM_P4_CHAR - elif [[ $SCM == "$SCM_HG" ]]; then - SCM_CHAR=$SCM_HG_CHAR - elif [[ $SCM == "$SCM_SVN" ]]; then - SCM_CHAR=$SCM_SVN_CHAR - else - SCM_CHAR=$SCM_NONE_CHAR +function scm_prompt_char() { + if [[ -z "${SCM:-}" ]]; then + scm fi + + case ${SCM?} in + "${SCM_GIT?}") + SCM_CHAR="${SCM_GIT_CHAR?}" + ;; + "${SCM_HG?}") + SCM_CHAR="${SCM_HG_CHAR?}" + ;; + "${SCM_SVN?}") + SCM_CHAR="${SCM_SVN_CHAR?}" + ;; + "${SCM_P4?}") + SCM_CHAR="${SCM_P4_CHAR?}" + ;; + *) + SCM_CHAR="${SCM_NONE_CHAR:-}" + ;; + esac } -function scm_prompt_vars { +function scm_prompt_vars() { scm scm_prompt_char SCM_DIRTY=0 SCM_STATE='' - [[ $SCM == "$SCM_GIT" ]] && git_prompt_vars && return - [[ $SCM == "$SCM_P4" ]] && p4_prompt_vars && return - [[ $SCM == "$SCM_HG" ]] && hg_prompt_vars && return - [[ $SCM == "$SCM_SVN" ]] && svn_prompt_vars && return + + local prompt_vars="${SCM}_prompt_vars" + _is_function "${prompt_vars}" && "${prompt_vars}" } -function scm_prompt_info { +function scm_prompt_info() { scm scm_prompt_char scm_prompt_info_common } -function scm_prompt_char_info { +function scm_prompt_char_info() { scm_prompt_char - echo -ne "${SCM_THEME_CHAR_PREFIX}${SCM_CHAR}${SCM_THEME_CHAR_SUFFIX}" + echo -ne "${SCM_THEME_CHAR_PREFIX-}${SCM_CHAR?}${SCM_THEME_CHAR_SUFFIX-}" scm_prompt_info_common } -function scm_prompt_info_common { +function scm_prompt_info_common() { + local prompt_info SCM_DIRTY=0 SCM_STATE='' - if [[ ${SCM} == "${SCM_GIT}" ]]; then - if [[ ${SCM_GIT_SHOW_MINIMAL_INFO} == true ]]; then - # user requests minimal git status information - git_prompt_minimal_info - else - # more detailed git status - git_prompt_info - fi - return - fi - - # TODO: consider adding minimal status information for hg and svn - { [[ ${SCM} == "${SCM_P4}" ]] && p4_prompt_info && return; } || true - { [[ ${SCM} == "${SCM_HG}" ]] && hg_prompt_info && return; } || true - { [[ ${SCM} == "${SCM_SVN}" ]] && svn_prompt_info && return; } || true + case ${SCM?} in + "${SCM_GIT?}") + if [[ ${SCM_GIT_SHOW_MINIMAL_INFO:-false} == "true" ]]; then + # user requests minimal git status information + prompt_info="${SCM}_prompt_minimal_info" + else + # more detailed git status + prompt_info="${SCM}_prompt_info" + fi + ;; + *) + # TODO: consider adding minimal status information for hg and svn + prompt_info="${SCM}_prompt_info" + ;; + esac + _is_function "${prompt_info}" && "${prompt_info}" } -function terraform_workspace_prompt { +function terraform_workspace_prompt() { if _command_exists terraform; then - if [ -d .terraform ]; then - echo -e "$(terraform workspace show 2> /dev/null)" + if [[ -d .terraform ]]; then + terraform workspace show 2> /dev/null fi fi } -function active_gcloud_account_prompt { +function active_gcloud_account_prompt() { if _command_exists gcloud; then - echo -e "$(gcloud config list account --format "value(core.account)" 2> /dev/null)" + gcloud config list account --format "value(core.account)" 2> /dev/null fi } -function git_prompt_minimal_info { - SCM_STATE=${SCM_THEME_PROMPT_CLEAN} +function git_prompt_minimal_info() { + SCM_STATE="${SCM_THEME_PROMPT_CLEAN?}" _git-hide-status && return - SCM_BRANCH="${SCM_THEME_BRANCH_PREFIX}\$(_git-friendly-ref)" + SCM_BRANCH="${SCM_THEME_BRANCH_PREFIX-}\$(_git-friendly-ref)" if [[ -n "$(_git-status | tail -n1)" ]]; then SCM_DIRTY=1 - SCM_STATE=${SCM_THEME_PROMPT_DIRTY} + SCM_STATE="${SCM_THEME_PROMPT_DIRTY?}" fi # Output the git prompt - SCM_PREFIX=${SCM_THEME_PROMPT_PREFIX} - SCM_SUFFIX=${SCM_THEME_PROMPT_SUFFIX} - echo -e "${SCM_PREFIX}${SCM_BRANCH}${SCM_STATE}${SCM_SUFFIX}" + SCM_PREFIX="${SCM_THEME_PROMPT_PREFIX-}" + SCM_SUFFIX="${SCM_THEME_PROMPT_SUFFIX-}" + echo -ne "${SCM_PREFIX}${SCM_BRANCH}${SCM_STATE}${SCM_SUFFIX}" } -function git_prompt_vars { - if ${SCM_GIT_USE_GITSTATUS} && _command_exists gitstatus_query && gitstatus_query && [[ "${VCS_STATUS_RESULT}" == "ok-sync" ]]; then +function git_prompt_vars() { + if [[ "${SCM_GIT_USE_GITSTATUS:-false}" != "false" ]] && _command_exists gitstatus_query && gitstatus_query && [[ "${VCS_STATUS_RESULT:-}" == "ok-sync" ]]; then # we can use faster gitstatus # use this variable in githelpers and below to choose gitstatus output SCM_GIT_GITSTATUS_RAN=true @@ -241,146 +261,130 @@ function git_prompt_vars { local detached_prefix if _git-tag &> /dev/null; then - detached_prefix=${SCM_THEME_TAG_PREFIX} + detached_prefix="${SCM_THEME_TAG_PREFIX}" else - detached_prefix=${SCM_THEME_DETACHED_PREFIX} + detached_prefix="${SCM_THEME_DETACHED_PREFIX}" fi SCM_BRANCH="${detached_prefix}\$(_git-friendly-ref)" fi - if [[ "${SCM_GIT_GITSTATUS_RAN}" == "true" ]]; then - commits_behind=${VCS_STATUS_COMMITS_BEHIND} - commits_ahead=${VCS_STATUS_COMMITS_AHEAD} + if [[ "${SCM_GIT_GITSTATUS_RAN:-}" == "true" ]]; then + commits_behind="${VCS_STATUS_COMMITS_BEHIND?}" + commits_ahead="${VCS_STATUS_COMMITS_AHEAD?}" else IFS=$'\t' read -r commits_behind commits_ahead <<< "$(_git-upstream-behind-ahead)" fi if [[ "${commits_ahead}" -gt 0 ]]; then SCM_BRANCH+="${SCM_GIT_AHEAD_BEHIND_PREFIX_CHAR}${SCM_GIT_AHEAD_CHAR}" - [[ "${SCM_GIT_SHOW_COMMIT_COUNT}" = "true" ]] && SCM_BRANCH+="${commits_ahead}" + [[ "${SCM_GIT_SHOW_COMMIT_COUNT}" == "true" ]] && SCM_BRANCH+="${commits_ahead}" fi if [[ "${commits_behind}" -gt 0 ]]; then SCM_BRANCH+="${SCM_GIT_AHEAD_BEHIND_PREFIX_CHAR}${SCM_GIT_BEHIND_CHAR}" - [[ "${SCM_GIT_SHOW_COMMIT_COUNT}" = "true" ]] && SCM_BRANCH+="${commits_behind}" + [[ "${SCM_GIT_SHOW_COMMIT_COUNT}" == "true" ]] && SCM_BRANCH+="${commits_behind}" fi - if [[ "${SCM_GIT_SHOW_STASH_INFO}" = "true" ]]; then + if [[ "${SCM_GIT_SHOW_STASH_INFO}" == "true" ]]; then local stash_count if [[ "${SCM_GIT_GITSTATUS_RAN}" == "true" ]]; then - stash_count=${VCS_STATUS_STASHES} + stash_count="${VCS_STATUS_STASHES?}" else stash_count="$(git stash list 2> /dev/null | wc -l | tr -d ' ')" fi [[ "${stash_count}" -gt 0 ]] && SCM_BRANCH+=" ${SCM_GIT_STASH_CHAR_PREFIX}${stash_count}${SCM_GIT_STASH_CHAR_SUFFIX}" fi - SCM_STATE=${GIT_THEME_PROMPT_CLEAN:-$SCM_THEME_PROMPT_CLEAN} + SCM_STATE="${GIT_THEME_PROMPT_CLEAN:-${SCM_THEME_PROMPT_CLEAN:-}}" if ! _git-hide-status; then - if [[ "${SCM_GIT_GITSTATUS_RAN}" == "true" ]]; then - untracked_count=${VCS_STATUS_NUM_UNTRACKED} - unstaged_count=${VCS_STATUS_NUM_UNSTAGED} - staged_count=${VCS_STATUS_NUM_STAGED} + if [[ "${SCM_GIT_GITSTATUS_RAN:-}" == "true" ]]; then + untracked_count="${VCS_STATUS_NUM_UNTRACKED?}" + unstaged_count="${VCS_STATUS_NUM_UNSTAGED?}" + staged_count="${VCS_STATUS_NUM_STAGED?}" else - IFS=$'\t' read -r untracked_count unstaged_count staged_count <<< "$(_git-status-counts)" + IFS=$'\t' read -r untracked_count unstaged_count staged_count < <(_git-status-counts) fi if [[ "${untracked_count}" -gt 0 || "${unstaged_count}" -gt 0 || "${staged_count}" -gt 0 ]]; then SCM_DIRTY=1 - if [[ "${SCM_GIT_SHOW_DETAILS}" = "true" ]]; then + if [[ "${SCM_GIT_SHOW_DETAILS}" == "true" ]]; then [[ "${staged_count}" -gt 0 ]] && SCM_BRANCH+=" ${SCM_GIT_STAGED_CHAR}${staged_count}" && SCM_DIRTY=3 [[ "${unstaged_count}" -gt 0 ]] && SCM_BRANCH+=" ${SCM_GIT_UNSTAGED_CHAR}${unstaged_count}" && SCM_DIRTY=2 [[ "${untracked_count}" -gt 0 ]] && SCM_BRANCH+=" ${SCM_GIT_UNTRACKED_CHAR}${untracked_count}" && SCM_DIRTY=1 fi - SCM_STATE=${GIT_THEME_PROMPT_DIRTY:-$SCM_THEME_PROMPT_DIRTY} + SCM_STATE="${GIT_THEME_PROMPT_DIRTY:-${SCM_THEME_PROMPT_DIRTY?}}" fi fi # no if for gitstatus here, user extraction is not supported by it [[ "${SCM_GIT_SHOW_CURRENT_USER}" == "true" ]] && SCM_BRANCH+="$(git_user_info)" - SCM_PREFIX=${GIT_THEME_PROMPT_PREFIX:-$SCM_THEME_PROMPT_PREFIX} - SCM_SUFFIX=${GIT_THEME_PROMPT_SUFFIX:-$SCM_THEME_PROMPT_SUFFIX} + SCM_PREFIX="${GIT_THEME_PROMPT_PREFIX:-${SCM_THEME_PROMPT_PREFIX-}}" + SCM_SUFFIX="${GIT_THEME_PROMPT_SUFFIX:-${SCM_THEME_PROMPT_SUFFIX-}}" - SCM_CHANGE=$(_git-short-sha 2> /dev/null || echo "") + SCM_CHANGE=$(_git-short-sha 2> /dev/null || true) } -function p4_prompt_vars { +function p4_prompt_vars() { + local opened_count non_default_changes default_count \ + add_file_count edit_file_count delete_file_count + IFS=$'\t' read -r \ opened_count non_default_changes default_count \ add_file_count edit_file_count delete_file_count \ - <<< "$(_p4-opened-counts)" + < <(_p4-opened-counts) if [[ "${opened_count}" -gt 0 ]]; then SCM_DIRTY=1 - SCM_STATE=${SCM_THEME_PROMPT_DIRTY} - [[ "${opened_count}" -gt 0 ]] && SCM_BRANCH+=" ${SCM_P4_OPENED_CHAR}${opened_count}" - [[ "${non_default_changes}" -gt 0 ]] && SCM_BRANCH+=" ${SCM_P4_CHANGES_CHAR}${non_default_changes}" - [[ "${default_count}" -gt 0 ]] && SCM_BRANCH+=" ${SCM_P4_DEFAULT_CHAR}${default_count}" + SCM_STATE="${SCM_THEME_PROMPT_DIRTY?}" + [[ "${opened_count}" -gt 0 ]] && SCM_BRANCH+=" ${SCM_P4_OPENED_CHAR?}${opened_count}" + [[ "${non_default_changes}" -gt 0 ]] && SCM_BRANCH+=" ${SCM_P4_CHANGES_CHAR?}${non_default_changes}" + [[ "${default_count}" -gt 0 ]] && SCM_BRANCH+=" ${SCM_P4_DEFAULT_CHAR?}${default_count}" else SCM_DIRTY=0 - SCM_STATE=${SCM_THEME_PROMPT_DIRTY} + SCM_STATE="${SCM_THEME_PROMPT_CLEAN?}" fi - SCM_PREFIX=${P4_THEME_PROMPT_PREFIX:-$SCM_THEME_PROMPT_PREFIX} - SCM_SUFFIX=${P4_THEME_PROMPT_SUFFIX:-$SCM_THEME_PROMPT_SUFFIX} + SCM_PREFIX="${P4_THEME_PROMPT_PREFIX:-${SCM_THEME_PROMPT_PREFIX-}}" + SCM_SUFFIX="${P4_THEME_PROMPT_SUFFIX:-${SCM_THEME_PROMPT_SUFFIX-}}" } -function svn_prompt_vars { - if [[ -n $(svn status | head -c1 2> /dev/null) ]]; then +function svn_prompt_vars() { + if [[ -n "$(svn status | head -c1 2> /dev/null)" ]]; then SCM_DIRTY=1 - SCM_STATE=${SVN_THEME_PROMPT_DIRTY:-$SCM_THEME_PROMPT_DIRTY} + SCM_STATE="${SVN_THEME_PROMPT_DIRTY:-${SCM_THEME_PROMPT_DIRTY?}}" else SCM_DIRTY=0 - SCM_STATE=${SVN_THEME_PROMPT_CLEAN:-$SCM_THEME_PROMPT_CLEAN} + SCM_STATE="${SVN_THEME_PROMPT_CLEAN:-${SCM_THEME_PROMPT_CLEAN?}}" fi - SCM_PREFIX=${SVN_THEME_PROMPT_PREFIX:-$SCM_THEME_PROMPT_PREFIX} - SCM_SUFFIX=${SVN_THEME_PROMPT_SUFFIX:-$SCM_THEME_PROMPT_SUFFIX} - SCM_BRANCH=$(svn info --show-item=url 2> /dev/null | awk -F/ '{ for (i=0; i<=NF; i++) { if ($i == "branches" || $i == "tags" ) { print $(i+1); break }; if ($i == "trunk") { print $i; break } } }') || return - SCM_CHANGE=$(svn info --show-item=revision 2> /dev/null) + SCM_PREFIX="${SVN_THEME_PROMPT_PREFIX:-${SCM_THEME_PROMPT_PREFIX-}}" + SCM_SUFFIX="${SVN_THEME_PROMPT_SUFFIX:-${SCM_THEME_PROMPT_SUFFIX-}}" + SCM_BRANCH="$(svn info --show-item=url 2> /dev/null | awk -F/ '{ for (i=0; i<=NF; i++) { if ($i == "branches" || $i == "tags" ) { print $(i+1); break }; if ($i == "trunk") { print $i; break } } }')" || return + SCM_CHANGE="$(svn info --show-item=revision 2> /dev/null)" } -# this functions returns absolute location of .hg directory if one exists -# It starts in the current directory and moves its way up until it hits /. -# If we get to / then no Mercurial repository was found. -# Example: -# - lets say we cd into ~/Projects/Foo/Bar -# - .hg is located in ~/Projects/Foo/.hg -# - get_hg_root starts at ~/Projects/Foo/Bar and sees that there is no .hg directory, so then it goes into ~/Projects/Foo -function get_hg_root { - local CURRENT_DIR=$(pwd) - - while [ "$CURRENT_DIR" != "/" ]; do - if [ -d "$CURRENT_DIR/.hg" ]; then - echo "$CURRENT_DIR/.hg" - return - fi - - CURRENT_DIR=$(dirname "$CURRENT_DIR") - done -} - -function hg_prompt_vars { +function hg_prompt_vars() { + local hg_root bookmark if [[ -n $(hg status 2> /dev/null) ]]; then SCM_DIRTY=1 - SCM_STATE=${HG_THEME_PROMPT_DIRTY:-$SCM_THEME_PROMPT_DIRTY} + SCM_STATE="${HG_THEME_PROMPT_DIRTY:-${SCM_THEME_PROMPT_DIRTY?}}" else SCM_DIRTY=0 - SCM_STATE=${HG_THEME_PROMPT_CLEAN:-$SCM_THEME_PROMPT_CLEAN} + SCM_STATE="${HG_THEME_PROMPT_CLEAN:-${SCM_THEME_PROMPT_CLEAN?}}" fi - SCM_PREFIX=${HG_THEME_PROMPT_PREFIX:-$SCM_THEME_PROMPT_PREFIX} - SCM_SUFFIX=${HG_THEME_PROMPT_SUFFIX:-$SCM_THEME_PROMPT_SUFFIX} + SCM_PREFIX="${HG_THEME_PROMPT_PREFIX:-${SCM_THEME_PROMPT_PREFIX-}}" + SCM_SUFFIX="${HG_THEME_PROMPT_SUFFIX:-${SCM_THEME_PROMPT_SUFFIX-}}" - HG_ROOT=$(get_hg_root) + hg_root="$(_bash-it-find-in-ancestor ".hg")/.hg" - if [ -f "$HG_ROOT/branch" ]; then + if [[ -f "$hg_root/branch" ]]; then # Mercurial holds it's current branch in .hg/branch file - SCM_BRANCH=$(< "${HG_ROOT}/branch") - local bookmark=${HG_ROOT}/bookmarks.current - [[ -f ${bookmark} ]] && SCM_BRANCH+=:$(< "${bookmark}") + SCM_BRANCH=$(< "${hg_root}/branch") + bookmark="${hg_root}/bookmarks.current" + [[ -f "${bookmark}" ]] && SCM_BRANCH+=:$(< "${bookmark}") else SCM_BRANCH=$(hg summary 2> /dev/null | grep branch: | awk '{print $2}') fi - if [ -f "$HG_ROOT/dirstate" ]; then + if [[ -f "$hg_root/dirstate" ]]; then # Mercurial holds various information about the working directory in .hg/dirstate file. More on http://mercurial.selenic.com/wiki/DirState - SCM_CHANGE=$(hexdump -vn 10 -e '1/1 "%02x"' "$HG_ROOT/dirstate" | cut -c-12) + SCM_CHANGE=$(hexdump -vn 10 -e '1/1 "%02x"' "$hg_root/dirstate" | cut -c-12) else SCM_CHANGE=$(hg summary 2> /dev/null | grep parent: | awk '{print $2}') fi @@ -407,43 +411,43 @@ function node_version_prompt() { fi } -function nvm_version_prompt { +function nvm_version_prompt() { local node - if declare -f -F nvm &> /dev/null; then + if _is_function nvm; then node=$(nvm current 2> /dev/null) [[ "${node}" == "system" ]] && return - echo -e "${NVM_THEME_PROMPT_PREFIX}${node}${NVM_THEME_PROMPT_SUFFIX}" + echo -ne "${NVM_THEME_PROMPT_PREFIX-}${node}${NVM_THEME_PROMPT_SUFFIX-}" fi } -function rvm_version_prompt { - if which rvm &> /dev/null; then - rvm=$(rvm-prompt) || return - if [ -n "$rvm" ]; then - echo -e "$RVM_THEME_PROMPT_PREFIX$rvm$RVM_THEME_PROMPT_SUFFIX" +function rvm_version_prompt() { + if _command_exists rvm; then + rvm="$(rvm-prompt)" || return + if [[ -n "$rvm" ]]; then + echo -ne "${RVM_THEME_PROMPT_PREFIX-}${rvm}${RVM_THEME_PROMPT_SUFFIX-}" fi fi } -function rbenv_version_prompt { - if which rbenv &> /dev/null; then +function rbenv_version_prompt() { + if _command_exists rbenv; then rbenv=$(rbenv version-name) || return rbenv commands | grep -q gemset && gemset=$(rbenv gemset active 2> /dev/null) && rbenv="$rbenv@${gemset%% *}" - if [ "$rbenv" != "system" ]; then - echo -e "$RBENV_THEME_PROMPT_PREFIX$rbenv$RBENV_THEME_PROMPT_SUFFIX" + if [[ "$rbenv" != "system" ]]; then + echo -ne "${RBENV_THEME_PROMPT_PREFIX-}${rbenv}${RBENV_THEME_PROMPT_SUFFIX-}" fi fi } -function rbfu_version_prompt { - if [[ $RBFU_RUBY_VERSION ]]; then - echo -e "${RBFU_THEME_PROMPT_PREFIX}${RBFU_RUBY_VERSION}${RBFU_THEME_PROMPT_SUFFIX}" +function rbfu_version_prompt() { + if [[ -n "${RBFU_RUBY_VERSION:-}" ]]; then + echo -ne "${RBFU_THEME_PROMPT_PREFIX-}${RBFU_RUBY_VERSION}${RBFU_THEME_PROMPT_SUFFIX-}" fi } -function chruby_version_prompt { - if declare -f -F chruby &> /dev/null; then - if declare -f -F chruby_auto &> /dev/null; then +function chruby_version_prompt() { + if _is_function chruby; then + if _is_function chruby_auto; then chruby_auto fi @@ -452,183 +456,147 @@ function chruby_version_prompt { if ! chruby | grep -q '\*'; then ruby_version="${ruby_version} (system)" fi - echo -e "${CHRUBY_THEME_PROMPT_PREFIX}${ruby_version}${CHRUBY_THEME_PROMPT_SUFFIX}" + echo -ne "${CHRUBY_THEME_PROMPT_PREFIX-}${ruby_version}${CHRUBY_THEME_PROMPT_SUFFIX-}" fi } -function ruby_version_prompt { - if [[ "${THEME_SHOW_RUBY_PROMPT}" = "true" ]]; then - echo -e "$(rbfu_version_prompt)$(rbenv_version_prompt)$(rvm_version_prompt)$(chruby_version_prompt)" +function ruby_version_prompt() { + if [[ "${THEME_SHOW_RUBY_PROMPT:-}" == "true" ]]; then + rbfu_version_prompt + rbenv_version_prompt + rvm_version_prompt + chruby_version_prompt fi } -function k8s_context_prompt { - echo -e "$(kubectl config current-context 2> /dev/null)" +function k8s_context_prompt() { + kubectl config current-context 2> /dev/null } -function k8s_namespace_prompt { - echo -e "$(kubectl config view --minify --output 'jsonpath={..namespace}' 2> /dev/null)" +function k8s_namespace_prompt() { + kubectl config view --minify --output 'jsonpath={..namespace}' 2> /dev/null } -function virtualenv_prompt { - if [[ -n "$VIRTUAL_ENV" ]]; then - virtualenv=$(basename "$VIRTUAL_ENV") - echo -e "$VIRTUALENV_THEME_PROMPT_PREFIX$virtualenv$VIRTUALENV_THEME_PROMPT_SUFFIX" +function virtualenv_prompt() { + local virtualenv + if [[ -n "${VIRTUAL_ENV:-}" ]]; then + virtualenv="${VIRTUAL_ENV##*/}" + echo -ne "${VIRTUALENV_THEME_PROMPT_PREFIX-}${virtualenv}${VIRTUALENV_THEME_PROMPT_SUFFIX-}" fi } -function condaenv_prompt { - if [[ $CONDA_DEFAULT_ENV ]]; then - echo -e "${CONDAENV_THEME_PROMPT_PREFIX}${CONDA_DEFAULT_ENV}${CONDAENV_THEME_PROMPT_SUFFIX}" +function condaenv_prompt() { + if [[ -n "${CONDA_DEFAULT_ENV:-}" ]]; then + echo -ne "${CONDAENV_THEME_PROMPT_PREFIX-}${CONDA_DEFAULT_ENV}${CONDAENV_THEME_PROMPT_SUFFIX-}" fi } -function py_interp_prompt { - py_version=$(python --version 2>&1 | awk 'NR==1{print "py-"$2;}') || return - echo -e "${PYTHON_THEME_PROMPT_PREFIX}${py_version}${PYTHON_THEME_PROMPT_SUFFIX}" +function py_interp_prompt() { + local py_version + py_version="$(python --version 2>&1 | awk 'NR==1{print "py-"$2;}')" || return + echo -ne "${PYTHON_THEME_PROMPT_PREFIX-}${py_version}${PYTHON_THEME_PROMPT_SUFFIX-}" } -function python_version_prompt { - echo -e "$(virtualenv_prompt)$(condaenv_prompt)$(py_interp_prompt)" +function python_version_prompt() { + virtualenv_prompt + condaenv_prompt + py_interp_prompt } -function git_user_info { +function git_user_info() { + local current_user # support two or more initials, set by 'git pair' plugin - SCM_CURRENT_USER=$(git config user.initials | sed 's% %+%') + current_user="$(git config user.initials | sed 's% %+%')" # if `user.initials` weren't set, attempt to extract initials from `user.name` - [[ -z "${SCM_CURRENT_USER}" ]] && SCM_CURRENT_USER=$(printf "%s" "$(for word in $(git config user.name | PERLIO=:utf8 perl -pe '$_=lc'); do printf "%s" "${word:0:1}"; done)") - [[ -n "${SCM_CURRENT_USER}" ]] && printf "%s" "$SCM_THEME_CURRENT_USER_PREFFIX$SCM_CURRENT_USER$SCM_THEME_CURRENT_USER_SUFFIX" + [[ -z "${current_user}" ]] && current_user=$(printf "%s" "$(for word in $(git config user.name | PERLIO=:utf8 perl -pe '$_=lc'); do printf "%s" "${word:0:1}"; done)") + [[ -n "${current_user}" ]] && printf "%s" "${SCM_THEME_CURRENT_USER_PREFFIX-}${current_user}${SCM_THEME_CURRENT_USER_SUFFIX-}" } -function clock_char { - CLOCK_CHAR=${THEME_CLOCK_CHAR:-"⌚"} - CLOCK_CHAR_COLOR=${THEME_CLOCK_CHAR_COLOR:-"$normal"} - SHOW_CLOCK_CHAR=${THEME_SHOW_CLOCK_CHAR:-"true"} +function clock_char() { + local clock_char clock_char_color show_clock_char + clock_char="${THEME_CLOCK_CHAR:-⌚}" + clock_char_color="${THEME_CLOCK_CHAR_COLOR:-${normal:-}}" + show_clock_char="${THEME_SHOW_CLOCK_CHAR:-"true"}" - if [[ "${SHOW_CLOCK_CHAR}" = "true" ]]; then - echo -e "${CLOCK_CHAR_COLOR}${CLOCK_CHAR_THEME_PROMPT_PREFIX}${CLOCK_CHAR}${CLOCK_CHAR_THEME_PROMPT_SUFFIX}" + if [[ "${show_clock_char}" == "true" ]]; then + echo -ne "${clock_char_color}${CLOCK_CHAR_THEME_PROMPT_PREFIX-}${clock_char}${CLOCK_CHAR_THEME_PROMPT_SUFFIX-}" fi } -function clock_prompt { - CLOCK_COLOR=${THEME_CLOCK_COLOR:-"$normal"} - CLOCK_FORMAT=${THEME_CLOCK_FORMAT:-"%H:%M:%S"} - [ -z "$THEME_SHOW_CLOCK" ] && THEME_SHOW_CLOCK=${THEME_CLOCK_CHECK:-"true"} - SHOW_CLOCK=$THEME_SHOW_CLOCK +function clock_prompt() { + local clock_color="${THEME_CLOCK_COLOR:-${normal?}}" + local clock_format="${THEME_CLOCK_FORMAT:-"%H:%M:%S"}" + local show_clock="${THEME_SHOW_CLOCK:-${THEME_CLOCK_CHECK:-true}}" + local clock_string="\D{${clock_format}}" - if [[ "${SHOW_CLOCK}" = "true" ]]; then - CLOCK_STRING=$(date +"${CLOCK_FORMAT}") - echo -e "${CLOCK_COLOR}${CLOCK_THEME_PROMPT_PREFIX}${CLOCK_STRING}${CLOCK_THEME_PROMPT_SUFFIX}" + if [[ "${show_clock}" == "true" ]]; then + echo -ne "${clock_color}${CLOCK_THEME_PROMPT_PREFIX-}${clock_string}${CLOCK_THEME_PROMPT_SUFFIX-}" fi } -function user_host_prompt { - if [[ "${THEME_SHOW_USER_HOST}" = "true" ]]; then - echo -e "${USER_HOST_THEME_PROMPT_PREFIX}\u@\h${USER_HOST_THEME_PROMPT_SUFFIX}" +function user_host_prompt() { + if [[ "${THEME_SHOW_USER_HOST:-false}" == "true" ]]; then + echo -ne "${USER_HOST_THEME_PROMPT_PREFIX-}\u@${THEME_PROMPT_HOST:-\h}${USER_HOST_THEME_PROMPT_SUFFIX-}" fi } # backwards-compatibility -function git_prompt_info { +function git_prompt_info() { _git-hide-status && return git_prompt_vars - echo -e "${SCM_PREFIX}${SCM_BRANCH}${SCM_STATE}${SCM_SUFFIX}" + echo -ne "${SCM_PREFIX?}${SCM_BRANCH?}${SCM_STATE?}${SCM_SUFFIX?}" } function p4_prompt_info() { p4_prompt_vars - echo -e "${SCM_PREFIX}${SCM_BRANCH}:${SCM_CHANGE}${SCM_STATE}${SCM_SUFFIX}" + echo -ne "${SCM_PREFIX?}${SCM_BRANCH?}:${SCM_CHANGE?}${SCM_STATE?}${SCM_SUFFIX?}" } -function svn_prompt_info { +function svn_prompt_info() { svn_prompt_vars - echo -e "${SCM_PREFIX}${SCM_BRANCH}${SCM_STATE}${SCM_SUFFIX}" + echo -ne "${SCM_PREFIX?}${SCM_BRANCH?}${SCM_STATE?}${SCM_SUFFIX?}" } function hg_prompt_info() { hg_prompt_vars - echo -e "${SCM_PREFIX}${SCM_BRANCH}:${SCM_CHANGE#*:}${SCM_STATE}${SCM_SUFFIX}" + echo -ne "${SCM_PREFIX?}${SCM_BRANCH?}:${SCM_CHANGE#*:}${SCM_STATE?}${SCM_SUFFIX?}" } -function scm_char { +function scm_char() { scm_prompt_char - echo -e "${SCM_THEME_CHAR_PREFIX}${SCM_CHAR}${SCM_THEME_CHAR_SUFFIX}" + echo -ne "${SCM_THEME_CHAR_PREFIX?}${SCM_CHAR?}${SCM_THEME_CHAR_SUFFIX?}" } -function prompt_char { +function prompt_char() { scm_char } -function battery_char { - if [[ "${THEME_BATTERY_PERCENTAGE_CHECK}" = true ]]; then - echo -e "${bold_red}$(battery_percentage)%" +function battery_char() { + # The battery_char function depends on the presence of the battery_percentage function. + if [[ "${THEME_BATTERY_PERCENTAGE_CHECK}" == true ]] && _command_exists battery_percentage; then + echo -ne "${bold_red?}$(battery_percentage)%" + else + false fi } if ! _command_exists battery_charge; then # if user has installed battery plugin, skip this... function battery_charge() { - # no op - echo -n + : # no op } fi -# The battery_char function depends on the presence of the battery_percentage function. -# If battery_percentage is not defined, then define battery_char as a no-op. -if ! _command_exists battery_percentage; then - function battery_char() { - # no op - echo -n - } -fi - -function aws_profile { - if [[ $AWS_DEFAULT_PROFILE ]]; then - echo -e "${AWS_DEFAULT_PROFILE}" +function aws_profile() { + if [[ -n "${AWS_DEFAULT_PROFILE:-}" ]]; then + echo -ne "${AWS_DEFAULT_PROFILE}" else - echo -e "default" - fi -} - -function __check_precmd_conflict() { - local f - for f in "${precmd_functions[@]}"; do - if [[ "${f}" == "${1}" ]]; then - return 0 - fi - done - return 1 -} - -function safe_append_prompt_command { - local prompt_re - - if [ "${__bp_imported}" == "defined" ]; then - # We are using bash-preexec - if ! __check_precmd_conflict "${1}"; then - precmd_functions+=("${1}") - fi - else - # Set OS dependent exact match regular expression - if [[ ${OSTYPE} == darwin* ]]; then - # macOS - prompt_re="[[:<:]]${1}[[:>:]]" - else - # Linux, FreeBSD, etc. - prompt_re="\<${1}\>" - fi - - if [[ ${PROMPT_COMMAND} =~ ${prompt_re} ]]; then - return - elif [[ -z ${PROMPT_COMMAND} ]]; then - PROMPT_COMMAND="${1}" - else - PROMPT_COMMAND="${1};${PROMPT_COMMAND}" - fi + echo -ne "default" fi } function _save-and-reload-history() { - local autosave=${1:-0} - [[ $autosave -eq 1 ]] && history -a && history -c && history -r + local autosave="${1:-${HISTORY_AUTOSAVE:-0}}" + [[ ${autosave} -eq 1 ]] && local HISTCONTROL="${HISTCONTROL:-}${HISTCONTROL:+:}autoshare" + _bash-it-history-auto-save && _bash-it-history-auto-load } diff --git a/themes/binaryanomaly/binaryanomaly.theme.bash b/themes/binaryanomaly/binaryanomaly.theme.bash index c2efa237..c4488c4c 100644 --- a/themes/binaryanomaly/binaryanomaly.theme.bash +++ b/themes/binaryanomaly/binaryanomaly.theme.bash @@ -1,4 +1,6 @@ # shellcheck shell=bash +# shellcheck disable=SC2034 # Expected behavior for themes. +# shellcheck disable=SC2154 #TODO: fix these all. # Detect whether a reboot is required function show_reboot_required() { diff --git a/themes/bira/bira.theme.bash b/themes/bira/bira.theme.bash index ae8f0efe..f30d8d5d 100644 --- a/themes/bira/bira.theme.bash +++ b/themes/bira/bira.theme.bash @@ -1,22 +1,26 @@ # shellcheck shell=bash +# shellcheck disable=SC2034 # Expected behavior for themes. -SCM_THEME_PROMPT_PREFIX=" ${yellow}‹" -SCM_THEME_PROMPT_SUFFIX="›${reset_color}" +SCM_THEME_PROMPT_PREFIX=" ${yellow?}‹" +SCM_THEME_PROMPT_SUFFIX="›${reset_color?}" -VIRTUALENV_THEME_PROMPT_PREFIX=" ${cyan}‹" -VIRTUALENV_THEME_PROMPT_SUFFIX="›${reset_color}" +VIRTUALENV_THEME_PROMPT_PREFIX=" ${cyan?}‹" +VIRTUALENV_THEME_PROMPT_SUFFIX="›${reset_color?}" bold="\[\e[1m\]" -if [ ${UID} -eq 0 ]; then - user_host="${bold_red}\u@\h${normal}${reset_color}" +if [[ ${UID} -eq 0 ]]; then + user_host="${bold_red?}\u@\h${normal?}${reset_color?}" else - user_host="${bold_green}\u@\h${normal}${reset_color}" + user_host="${bold_green?}\u@\h${normal?}${reset_color?}" fi function prompt_command() { - local current_dir=" ${bold_blue}\w${normal}${reset_color}" - PS1="╭─${user_host}${current_dir}$(virtualenv_prompt)$(scm_prompt_info)\n╰─${bold}\\$ ${normal}" + local current_dir=" ${bold_blue?}\w${normal?}${reset_color?}" + local virtualenv_prompt scm_prompt_info + virtualenv_prompt="$(virtualenv_prompt)" + scm_prompt_info="$(scm_prompt_info)" + PS1="╭─${user_host?}${current_dir}${virtualenv_prompt}${scm_prompt_info}\n╰─${bold?}\\$ ${normal?}" } safe_append_prompt_command prompt_command diff --git a/themes/bobby-python/bobby-python.theme.bash b/themes/bobby-python/bobby-python.theme.bash index 9144e251..5a46b258 100644 --- a/themes/bobby-python/bobby-python.theme.bash +++ b/themes/bobby-python/bobby-python.theme.bash @@ -1,25 +1,26 @@ # shellcheck shell=bash +# shellcheck disable=SC2034 # Expected behavior for themes. -SCM_THEME_PROMPT_DIRTY=" ${red}✗" -SCM_THEME_PROMPT_CLEAN=" ${bold_green}✓" +SCM_THEME_PROMPT_DIRTY=" ${red?}✗" +SCM_THEME_PROMPT_CLEAN=" ${bold_green?}✓" SCM_THEME_PROMPT_PREFIX=" |" -SCM_THEME_PROMPT_SUFFIX="${green}|" +SCM_THEME_PROMPT_SUFFIX="${green?}|" -GIT_THEME_PROMPT_DIRTY=" ${red}✗" -GIT_THEME_PROMPT_CLEAN=" ${bold_green}✓" -GIT_THEME_PROMPT_PREFIX=" ${green}|" -GIT_THEME_PROMPT_SUFFIX="${green}|" +GIT_THEME_PROMPT_DIRTY=" ${red?}✗" +GIT_THEME_PROMPT_CLEAN=" ${bold_green?}✓" +GIT_THEME_PROMPT_PREFIX=" ${green?}|" +GIT_THEME_PROMPT_SUFFIX="${green?}|" CONDAENV_THEME_PROMPT_SUFFIX="|" function prompt_command() { - PS1="\n${yellow}$(python_version_prompt) " # Name of virtual env followed by python version - PS1+="${purple}\h " - PS1+="${reset_color}in " - PS1+="${green}\w\n" - PS1+="${bold_cyan}$(scm_char)" - PS1+="${green}$(scm_prompt_info) " - PS1+="${green}→${reset_color} " + PS1="\n${yellow?}$(python_version_prompt) " # Name of virtual env followed by python version + PS1+="${purple?}\h " + PS1+="${reset_color?}in " + PS1+="${green?}\w\n" + PS1+="${bold_cyan?}$(scm_char)" + PS1+="${green?}$(scm_prompt_info) " + PS1+="${green?}→${reset_color?} " } safe_append_prompt_command prompt_command diff --git a/themes/bobby/bobby.theme.bash b/themes/bobby/bobby.theme.bash index bdf388f7..08f54c41 100644 --- a/themes/bobby/bobby.theme.bash +++ b/themes/bobby/bobby.theme.bash @@ -1,39 +1,40 @@ # shellcheck shell=bash +# shellcheck disable=SC2034 # Expected behavior for themes. -SCM_THEME_PROMPT_DIRTY=" ${red}✗" -SCM_THEME_PROMPT_CLEAN=" ${bold_green}✓" -SCM_THEME_PROMPT_PREFIX=" ${green}|" -SCM_THEME_PROMPT_SUFFIX="${green}|" +SCM_THEME_PROMPT_DIRTY=" ${red?}✗" +SCM_THEME_PROMPT_CLEAN=" ${bold_green?}✓" +SCM_THEME_PROMPT_PREFIX=" ${green?}|" +SCM_THEME_PROMPT_SUFFIX="${green?}|" -GIT_THEME_PROMPT_DIRTY=" ${red}✗" -GIT_THEME_PROMPT_CLEAN=" ${bold_green}✓" -GIT_THEME_PROMPT_PREFIX=" ${green}|" -GIT_THEME_PROMPT_SUFFIX="${green}|" +GIT_THEME_PROMPT_DIRTY=" ${red?}✗" +GIT_THEME_PROMPT_CLEAN=" ${bold_green?}✓" +GIT_THEME_PROMPT_PREFIX=" ${green?}|" +GIT_THEME_PROMPT_SUFFIX="${green?}|" RVM_THEME_PROMPT_PREFIX="|" RVM_THEME_PROMPT_SUFFIX="|" -__bobby_clock() { +function __bobby_clock() { printf '%s' "$(clock_prompt) " - if [ "${THEME_SHOW_CLOCK_CHAR}" == "true" ]; then + if [[ "${THEME_SHOW_CLOCK_CHAR:-}" == "true" ]]; then printf '%s' "$(clock_char) " fi } function prompt_command() { PS1="\n$(battery_char) $(__bobby_clock)" - PS1+="${yellow}$(ruby_version_prompt) " - PS1+="${purple}\h " - PS1+="${reset_color}in " - PS1+="${green}\w\n" - PS1+="${bold_cyan}$(scm_prompt_char_info) " - PS1+="${green}→${reset_color} " + PS1+="${yellow?}$(ruby_version_prompt) " + PS1+="${purple?}\h " + PS1+="${reset_color?}in " + PS1+="${green?}\w\n" + PS1+="${bold_cyan?}$(scm_prompt_char_info) " + PS1+="${green?}→${reset_color?} " } -THEME_SHOW_CLOCK_CHAR=${THEME_SHOW_CLOCK_CHAR:-"true"} -THEME_CLOCK_CHAR_COLOR=${THEME_CLOCK_CHAR_COLOR:-"$red"} -THEME_CLOCK_COLOR=${THEME_CLOCK_COLOR:-"$bold_cyan"} -THEME_CLOCK_FORMAT=${THEME_CLOCK_FORMAT:-"%Y-%m-%d %H:%M:%S"} +: "${THEME_SHOW_CLOCK_CHAR:="true"}" +: "${THEME_CLOCK_CHAR_COLOR:=${red?}}" +: "${THEME_CLOCK_COLOR:=${bold_cyan?}}" +: "${THEME_CLOCK_FORMAT:="%Y-%m-%d %H:%M:%S"}" safe_append_prompt_command prompt_command diff --git a/themes/brainy/brainy.theme.bash b/themes/brainy/brainy.theme.bash index db8377ba..e1c36175 100644 --- a/themes/brainy/brainy.theme.bash +++ b/themes/brainy/brainy.theme.bash @@ -1,4 +1,6 @@ # shellcheck shell=bash +# shellcheck disable=SC2034 # Expected behavior for themes. +# shellcheck disable=SC2154 #TODO: fix these all. # Brainy Bash Prompt for Bash-it # by MunifTanjim diff --git a/themes/brunton/brunton.theme.bash b/themes/brunton/brunton.theme.bash index 27b822ca..04dcbb3e 100644 --- a/themes/brunton/brunton.theme.bash +++ b/themes/brunton/brunton.theme.bash @@ -1,26 +1,31 @@ # shellcheck shell=bash +# shellcheck disable=SC2034 # Expected behavior for themes. SCM_THEME_PROMPT_PREFIX="" SCM_THEME_PROMPT_SUFFIX="" -SCM_THEME_PROMPT_DIRTY=" ${bold_red}✗${normal}" -SCM_THEME_PROMPT_CLEAN=" ${bold_green}✓${normal}" -SCM_GIT_CHAR="${bold_green}±${normal}" -SCM_SVN_CHAR="${bold_cyan}⑆${normal}" -SCM_HG_CHAR="${bold_red}☿${normal}" +SCM_THEME_PROMPT_DIRTY=" ${bold_red?}✗${normal?}" +SCM_THEME_PROMPT_CLEAN=" ${bold_green?}✓${normal?}" +SCM_GIT_CHAR="${bold_green?}±${normal?}" +SCM_SVN_CHAR="${bold_cyan?}⑆${normal?}" +SCM_HG_CHAR="${bold_red?}☿${normal?}" -is_vim_shell() { - if [ -n "$VIMRUNTIME" ]; then - echo "[${cyan}vim shell${normal}]" +function is_vim_shell() { + if [[ -n "${VIMRUNTIME:-}" ]]; then + echo "[${cyan?}vim shell${normal?}]" fi } -prompt() { - SCM_PROMPT_FORMAT=' %s (%s)' - PS1="${white}${background_blue} \u${normal}${background_blue}@${red}${background_blue}\h $(clock_prompt) ${reset_color}${normal} $(battery_charge)\n${bold_black}${background_white} \w ${normal}$(scm_prompt)$(is_vim_shell)\n${white}>${normal} " +function prompt() { + local SCM_PROMPT_FORMAT=' %s (%s)' clock_prompt battery_charge scm_prompt is_vim_shell + clock_prompt="$(clock_prompt)" + battery_charge="$(battery_charge)" + scm_prompt="$(scm_prompt)" + is_vim_shell="$(is_vim_shell)" + PS1="${white?}${background_blue?} \u${normal?}${background_blue?}@${red?}${background_blue?}\h ${clock_prompt} ${reset_color?}${normal?} ${battery_charge}\n${bold_black?}${background_white?} \w ${normal?}${scm_prompt}${is_vim_shell}\n${white?}>${normal?} " } -THEME_CLOCK_COLOR=${THEME_CLOCK_COLOR:-"$blue$background_white"} -THEME_CLOCK_FORMAT=${THEME_CLOCK_FORMAT:-" %H:%M:%S"} +: "${THEME_CLOCK_COLOR:=${blue?}${background_white?}}" +: "${THEME_CLOCK_FORMAT:=" %H:%M:%S"}" safe_append_prompt_command prompt diff --git a/themes/candy/candy.theme.bash b/themes/candy/candy.theme.bash index be53d373..69633bd9 100644 --- a/themes/candy/candy.theme.bash +++ b/themes/candy/candy.theme.bash @@ -1,10 +1,14 @@ # shellcheck shell=bash +# shellcheck disable=SC2034 # Expected behavior for themes. function prompt_command() { - PS1="${green}\u@\h $(clock_prompt) ${reset_color}${white}\w${reset_color}$(scm_prompt_info)${blue} →${bold_blue} ${reset_color} ${normal}" + local clock_prompt scm_prompt_info + clock_prompt="$(clock_prompt)" + scm_prompt_info="$(scm_prompt_info)" + PS1="${green?}\u@\h ${clock_prompt} ${reset_color?}${white?}\w${reset_color?}${scm_prompt_info}${blue?} →${bold_blue?} ${reset_color?} ${normal?}" } -THEME_CLOCK_COLOR=${THEME_CLOCK_COLOR:-"$blue"} -THEME_CLOCK_FORMAT=${THEME_CLOCK_FORMAT:-"%I:%M:%S"} +: "${THEME_CLOCK_COLOR:=${blue?}}" +: "${THEME_CLOCK_FORMAT:="%I:%M:%S"}" safe_append_prompt_command prompt_command diff --git a/themes/codeword/codeword.theme.bash b/themes/codeword/codeword.theme.bash index e23a3fc3..d52403c1 100644 --- a/themes/codeword/codeword.theme.bash +++ b/themes/codeword/codeword.theme.bash @@ -1,29 +1,25 @@ -SCM_THEME_PROMPT_PREFIX=${SCM_THEME_PROMPT_SUFFIX} -SCM_THEME_PROMPT_DIRTY="${bold_red} ✗${normal}" -SCM_THEME_PROMPT_CLEAN="${bold_green} ✓${normal}" -SCM_GIT_CHAR="${green}±${normal}" +# shellcheck shell=bash -mark_prompt() { - echo "${green}\$${normal}" +SCM_THEME_PROMPT_PREFIX="${SCM_THEME_PROMPT_SUFFIX:-}" +SCM_THEME_PROMPT_DIRTY="${bold_red?} ✗${normal?}" +SCM_THEME_PROMPT_CLEAN="${bold_green?} ✓${normal?}" +SCM_GIT_CHAR="${green?}±${normal?}" + +function mark_prompt() { + echo "${green?}\$${normal?}" } -user_host_path_prompt() { - ps_user="${green}\u${normal}"; - ps_host="${blue}\H${normal}"; - ps_path="${yellow}\w${normal}"; - echo "$ps_user@$ps_host:$ps_path" +function user_host_path_prompt() { + ps_user="${green?}\u${normal?}"; + ps_host="${blue?}\H${normal?}"; + ps_path="${yellow?}\w${normal?}"; + echo "${ps_user?}@${ps_host?}:${ps_path?}" } -prompt() { - SCM_PROMPT_FORMAT=' [%s%s]' +function prompt() { + local SCM_PROMPT_FORMAT=' [%s%s]' PS1="$(user_host_path_prompt)$(virtualenv_prompt)$(scm_prompt) $(mark_prompt) " } -share_history() { - history -a - history -c - history -r -} - -safe_append_prompt_command share_history +safe_append_prompt_command '_save-and-reload-history 1' safe_append_prompt_command prompt diff --git a/themes/colors.theme.bash b/themes/colors.theme.bash deleted file mode 100644 index fbb1dc5c..00000000 --- a/themes/colors.theme.bash +++ /dev/null @@ -1,270 +0,0 @@ -#!/usr/bin/env bash - -function __ { - echo "$@" -} - -function __make_ansi { - next=$1; shift - echo "\[\e[$(__$next $@)m\]" -} - -function __make_echo { - next=$1; shift - echo "\033[$(__$next $@)m" -} - - -function __reset { - next=$1; shift - out="$(__$next $@)" - echo "0${out:+;${out}}" -} - -function __bold { - next=$1; shift - out="$(__$next $@)" - echo "${out:+${out};}1" -} - -function __faint { - next=$1; shift - out="$(__$next $@)" - echo "${out:+${out};}2" -} - -function __italic { - next=$1; shift - out="$(__$next $@)" - echo "${out:+${out};}3" -} - -function __underline { - next=$1; shift - out="$(__$next $@)" - echo "${out:+${out};}4" -} - -function __negative { - next=$1; shift - out="$(__$next $@)" - echo "${out:+${out};}7" -} - -function __crossed { - next=$1; shift - out="$(__$next $@)" - echo "${out:+${out};}8" -} - - -function __color_normal_fg { - echo "3$1" -} - -function __color_normal_bg { - echo "4$1" -} - -function __color_bright_fg { - echo "9$1" -} - -function __color_bright_bg { - echo "10$1" -} - - -function __color_black { - echo "0" -} - -function __color_red { - echo "1" -} - -function __color_green { - echo "2" -} - -function __color_yellow { - echo "3" -} - -function __color_blue { - echo "4" -} - -function __color_magenta { - echo "5" -} - -function __color_cyan { - echo "6" -} - -function __color_white { - echo "7" -} - -function __color_rgb { - r=$1 && g=$2 && b=$3 - [[ $r == $g && $g == $b ]] && echo $(( $r / 11 + 232 )) && return # gray range above 232 - echo "8;5;$(( ($r * 36 + $b * 6 + $g) / 51 + 16 ))" -} - -function __color { - color=$1; shift - case "$1" in - fg|bg) side="$1"; shift ;; - *) side=fg;; - esac - case "$1" in - normal|bright) mode="$1"; shift;; - *) mode=normal;; - esac - [[ $color == "rgb" ]] && rgb="$1 $2 $3"; shift 3 - - next=$1; shift - out="$(__$next $@)" - echo "$(__color_${mode}_${side} $(__color_${color} $rgb))${out:+;${out}}" -} - - -function __black { - echo "$(__color black $@)" -} - -function __red { - echo "$(__color red $@)" -} - -function __green { - echo "$(__color green $@)" -} - -function __yellow { - echo "$(__color yellow $@)" -} - -function __blue { - echo "$(__color blue $@)" -} - -function __magenta { - echo "$(__color magenta $@)" -} - -function __cyan { - echo "$(__color cyan $@)" -} - -function __white { - echo "$(__color white $@)" -} - -function __rgb { - echo "$(__color rgb $@)" -} - - -function __color_parse { - next=$1; shift - echo "$(__$next $@)" -} - -function color { - echo "$(__color_parse make_ansi $@)" -} - -function echo_color { - echo "$(__color_parse make_echo $@)" -} - - -black="\[\e[0;30m\]" -red="\[\e[0;31m\]" -green="\[\e[0;32m\]" -yellow="\[\e[0;33m\]" -blue="\[\e[0;34m\]" -purple="\[\e[0;35m\]" -cyan="\[\e[0;36m\]" -white="\[\e[0;37m\]" -orange="\[\e[0;91m\]" - -bold_black="\[\e[30;1m\]" -bold_red="\[\e[31;1m\]" -bold_green="\[\e[32;1m\]" -bold_yellow="\[\e[33;1m\]" -bold_blue="\[\e[34;1m\]" -bold_purple="\[\e[35;1m\]" -bold_cyan="\[\e[36;1m\]" -bold_white="\[\e[37;1m\]" -bold_orange="\[\e[91;1m\]" - -underline_black="\[\e[30;4m\]" -underline_red="\[\e[31;4m\]" -underline_green="\[\e[32;4m\]" -underline_yellow="\[\e[33;4m\]" -underline_blue="\[\e[34;4m\]" -underline_purple="\[\e[35;4m\]" -underline_cyan="\[\e[36;4m\]" -underline_white="\[\e[37;4m\]" -underline_orange="\[\e[91;4m\]" - -background_black="\[\e[40m\]" -background_red="\[\e[41m\]" -background_green="\[\e[42m\]" -background_yellow="\[\e[43m\]" -background_blue="\[\e[44m\]" -background_purple="\[\e[45m\]" -background_cyan="\[\e[46m\]" -background_white="\[\e[47;1m\]" -background_orange="\[\e[101m\]" - -normal="\[\e[0m\]" -reset_color="\[\e[39m\]" - -# These colors are meant to be used with `echo -e` -echo_black="\033[0;30m" -echo_red="\033[0;31m" -echo_green="\033[0;32m" -echo_yellow="\033[0;33m" -echo_blue="\033[0;34m" -echo_purple="\033[0;35m" -echo_cyan="\033[0;36m" -echo_white="\033[0;37;1m" -echo_orange="\033[0;91m" - -echo_bold_black="\033[30;1m" -echo_bold_red="\033[31;1m" -echo_bold_green="\033[32;1m" -echo_bold_yellow="\033[33;1m" -echo_bold_blue="\033[34;1m" -echo_bold_purple="\033[35;1m" -echo_bold_cyan="\033[36;1m" -echo_bold_white="\033[37;1m" -echo_bold_orange="\033[91;1m" - -echo_underline_black="\033[30;4m" -echo_underline_red="\033[31;4m" -echo_underline_green="\033[32;4m" -echo_underline_yellow="\033[33;4m" -echo_underline_blue="\033[34;4m" -echo_underline_purple="\033[35;4m" -echo_underline_cyan="\033[36;4m" -echo_underline_white="\033[37;4m" -echo_underline_orange="\033[91;4m" - -echo_background_black="\033[40m" -echo_background_red="\033[41m" -echo_background_green="\033[42m" -echo_background_yellow="\033[43m" -echo_background_blue="\033[44m" -echo_background_purple="\033[45m" -echo_background_cyan="\033[46m" -echo_background_white="\033[47;1m" -echo_background_orange="\033[101m" - -echo_normal="\033[0m" -echo_reset_color="\033[39m" diff --git a/themes/command_duration.theme.bash b/themes/command_duration.theme.bash deleted file mode 100644 index cf91785c..00000000 --- a/themes/command_duration.theme.bash +++ /dev/null @@ -1,69 +0,0 @@ -# shellcheck shell=bash - -if [ -z "$BASH_IT_COMMAND_DURATION" ] || [ "$BASH_IT_COMMAND_DURATION" != true ]; then - _command_duration() { - echo -n - } - return -fi - -# Define tmp dir and file -COMMAND_DURATION_TMPDIR="${TMPDIR:-/tmp}" -COMMAND_DURATION_FILE="${COMMAND_DURATION_FILE:-$COMMAND_DURATION_TMPDIR/bashit_theme_execution_$BASHPID}" - -COMMAND_DURATION_ICON=${COMMAND_DURATION_ICON:-'  '} -COMMAND_DURATION_MIN_SECONDS=${COMMAND_DURATION_MIN_SECONDS:-'1'} - -trap _command_duration_delete_temp_file EXIT HUP INT TERM - -_command_duration_delete_temp_file() { - if [[ -f "$COMMAND_DURATION_FILE" ]]; then - rm -f "$COMMAND_DURATION_FILE" - fi -} - -_command_duration_pre_exec() { - date +%s.%1N > "$COMMAND_DURATION_FILE" -} - -_command_duration() { - local command_duration command_start current_time - local minutes seconds deciseconds - local command_start_sseconds current_time_seconds command_start_deciseconds current_time_deciseconds - current_time=$(date +%s.%1N) - - if [[ -f "$COMMAND_DURATION_FILE" ]]; then - command_start=$(< "$COMMAND_DURATION_FILE") - command_start_sseconds=${command_start%.*} - current_time_seconds=${current_time%.*} - - command_start_deciseconds=$((10#${command_start#*.})) - current_time_deciseconds=$((10#${current_time#*.})) - - # seconds - command_duration=$((current_time_seconds - command_start_sseconds)) - - 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)))) - fi - command rm "$COMMAND_DURATION_FILE" - else - command_duration=0 - fi - - if ((command_duration > 0)); then - minutes=$((command_duration / 60)) - seconds=$((command_duration % 60)) - fi - - if ((minutes > 0)); then - printf "%s%s%dm %ds" "$COMMAND_DURATION_ICON" "$COMMAND_DURATION_COLOR" "$minutes" "$seconds" - elif ((seconds >= COMMAND_DURATION_MIN_SECONDS)); then - printf "%s%s%d.%01ds" "$COMMAND_DURATION_ICON" "$COMMAND_DURATION_COLOR" "$seconds" "$deciseconds" - fi -} - -preexec_functions+=(_command_duration_pre_exec) diff --git a/themes/demula/demula.theme.bash b/themes/demula/demula.theme.bash old mode 100755 new mode 100644 diff --git a/themes/doubletime/doubletime.theme.bash b/themes/doubletime/doubletime.theme.bash index 6b6aa150..8186db73 100644 --- a/themes/doubletime/doubletime.theme.bash +++ b/themes/doubletime/doubletime.theme.bash @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +# shellcheck shell=bash SCM_THEME_PROMPT_DIRTY='' SCM_THEME_PROMPT_CLEAN='' @@ -30,9 +30,7 @@ fi function prompt_setter() { # Save history - history -a - history -c - history -r + _save-and-reload-history 1 PS1=" $(clock_prompt) $(scm_char) [${THEME_PROMPT_HOST_COLOR}\u@${THEME_PROMPT_HOST}$reset_color] $(virtualenv_prompt)$(ruby_version_prompt)\w $(scm_prompt)$reset_color $ " diff --git a/themes/doubletime_multiline/doubletime_multiline.theme.bash b/themes/doubletime_multiline/doubletime_multiline.theme.bash index 6da88253..f2dfe43a 100644 --- a/themes/doubletime_multiline/doubletime_multiline.theme.bash +++ b/themes/doubletime_multiline/doubletime_multiline.theme.bash @@ -1,16 +1,14 @@ -#!/usr/bin/env bash +# shellcheck shell=bash source "$BASH_IT/themes/doubletime/doubletime.theme.bash" function prompt_setter() { # Save history - history -a - history -c - history -r + _save-and-reload-history 1 PS1=" $(clock_prompt) $(scm_char) [$THEME_PROMPT_HOST_COLOR\u@${THEME_PROMPT_HOST}$reset_color] $(virtualenv_prompt)$(ruby_version_prompt) \w -$(doubletime_scm_prompt)$reset_color $ " +$(scm_prompt)$reset_color $ " PS2='> ' PS4='+ ' } diff --git a/themes/doubletime_multiline_pyonly/doubletime_multiline_pyonly.theme.bash b/themes/doubletime_multiline_pyonly/doubletime_multiline_pyonly.theme.bash index 5f1951e9..0fe0eff4 100644 --- a/themes/doubletime_multiline_pyonly/doubletime_multiline_pyonly.theme.bash +++ b/themes/doubletime_multiline_pyonly/doubletime_multiline_pyonly.theme.bash @@ -1,16 +1,14 @@ -#!/usr/bin/env bash +# shellcheck shell=bash source "$BASH_IT/themes/doubletime/doubletime.theme.bash" function prompt_setter() { # Save history - history -a - history -c - history -r + _save-and-reload-history 1 PS1=" $(clock_prompt) $(scm_char) [$THEME_PROMPT_HOST_COLOR\u@${THEME_PROMPT_HOST}$reset_color] $(virtualenv_prompt) \w -$(doubletime_scm_prompt)$reset_color $ " +$(scm_prompt)$reset_color $ " PS2='> ' PS4='+ ' } diff --git a/themes/dulcie/dulcie.theme.bash b/themes/dulcie/dulcie.theme.bash old mode 100644 new mode 100755 index a83b62fb..f70c7864 --- a/themes/dulcie/dulcie.theme.bash +++ b/themes/dulcie/dulcie.theme.bash @@ -77,7 +77,7 @@ dulcie_prompt() { printf "\033]0;%s@%s:%s\007" "${USER}" "${HOSTNAME%%.*}" "${PWD/#$HOME/\~}" # Open the new terminal in the same directory - declare -f __vte_osc7 > /dev/null && __vte_osc7 + _is_function __vte_osc7 && __vte_osc7 PS1="${reset_color}[${DULCIE_USER}@${DULCIE_HOST}$(scm_prompt_info)${reset_color} ${DULCIE_WORKINGDIR}]" if [[ "${DULCIE_MULTILINE}" -eq "1" ]]; then diff --git a/themes/easy/easy.theme.bash b/themes/easy/easy.theme.bash index a48eb824..3cb171d7 100644 --- a/themes/easy/easy.theme.bash +++ b/themes/easy/easy.theme.bash @@ -1,19 +1,22 @@ # shellcheck shell=bash +# shellcheck disable=SC2034 # Expected behavior for themes. -SCM_THEME_PROMPT_PREFIX="${bold_green}[ ${normal}" -SCM_THEME_PROMPT_SUFFIX="${bold_green} ] " -SCM_THEME_PROMPT_DIRTY=" ${red}✗" -SCM_THEME_PROMPT_CLEAN=" ${bold_green}✓" +SCM_THEME_PROMPT_PREFIX="${bold_green?}[ ${normal?}" +SCM_THEME_PROMPT_SUFFIX="${bold_green?} ] " +SCM_THEME_PROMPT_DIRTY=" ${red?}✗" +SCM_THEME_PROMPT_CLEAN=" ${bold_green?}✓" -prompt_command() { - if [ "$(whoami)" = root ]; then - cursor_color="${bold_red}" - user_color="${green}" +function prompt_command() { + local scm_prompt_info + if [ "${USER:-${LOGNAME?}}" = root ]; then + cursor_color="${bold_red?}" + user_color="${green?}" else - cursor_color="${bold_green}" - user_color="${white}" + cursor_color="${bold_green?}" + user_color="${white?}" fi - PS1="${user_color}\u${normal}@${white}\h ${bold_black}\w\n${reset_color}$(scm_prompt_info)${cursor_color}❯ ${normal}" + scm_prompt_info="$(scm_prompt_info)" + PS1="${user_color}\u${normal?}@${white?}\h ${bold_black?}\w\n${reset_color?}${scm_prompt_info}${cursor_color}❯ ${normal?}" } safe_append_prompt_command prompt_command diff --git a/themes/essential/essential.theme.bash b/themes/essential/essential.theme.bash index ae988733..05e84314 100644 --- a/themes/essential/essential.theme.bash +++ b/themes/essential/essential.theme.bash @@ -1,96 +1,93 @@ -#!/usr/bin/env bash - -# https://github.com/koalaman/shellcheck/wiki/Sc2154 -# shellcheck disable=SC2154 +# shellcheck shell=bash function _user-prompt() { - local -r user='\\u' + local -r user='\u' - if [[ "${EUID}" -eq 0 ]]; then - # Privileged users: - local -r user_color="${bold_red}" - else - # Standard users: - local -r user_color="${bold_green}" - fi + if [[ "${EUID}" -eq 0 ]]; then + # Privileged users: + local -r user_color="${bold_red?}" + else + # Standard users: + local -r user_color="${bold_green?}" + fi - # Print the current user's name (colored according to their current EUID): - echo -e "${user_color}${user}${normal}" + # Print the current user's name (colored according to their current EUID): + printf '%b%s%b' "${user_color}" "${user}" "${normal?}" } function _host-prompt() { - local -r host='\\h' + local -r host='\h' - # Check whether or not $SSH_TTY is set: - if [[ -z "${SSH_TTY}" ]]; then - # For local hosts, set the host's prompt color to blue: - local -r host_color="${bold_blue}" - else - # For remote hosts, set the host's prompt color to red: - local -r host_color="${bold_red}" - fi + # Check whether or not $SSH_TTY is set: + if [[ -z "${SSH_TTY:-}" ]]; then + # For local hosts, set the host's prompt color to blue: + local -r host_color="${bold_blue?}" + else + # For remote hosts, set the host's prompt color to red: + local -r host_color="${bold_red?}" + fi - # Print the current hostname (colored according to $SSH_TTY's status): - echo -e "${host_color}${host}${normal}" + # Print the current hostname (colored according to $SSH_TTY's status): + printf '%b%s%b' "${host_color}" "${host}" "${normal?}" } function _user-at-host-prompt() { - # Concatenate the user and host prompts into: user@host: - echo -e "$(_user-prompt)${bold_white}@$(_host-prompt)" + # Concatenate the user and host prompts into: user@host: + _user-prompt + printf '%b@' "${bold_white?}" + _host-prompt } function _exit-status-prompt() { - local -r prompt_string="${1}" - local -r exit_status="${2}" + local -r prompt_string="${1}" + local -r exit_status="${2}" - # Check the exit status of the last command captured by $exit_status: - if [[ "${exit_status}" -eq 0 ]]; then - # For commands that return an exit status of zero, set the exit status's - # notifier to green: - local -r exit_status_color="${bold_green}" - else - # For commands that return a non-zero exit status, set the exit status's - # notifier to red: - local -r exit_status_color="${bold_red}" - fi + # Check the exit status of the last command captured by $exit_status: + if [[ "${exit_status}" -eq 0 ]]; then + # For commands that return an exit status of zero, set the exit status's + # notifier to green: + local -r exit_status_color="${bold_green?}" + else + # For commands that return a non-zero exit status, set the exit status's + # notifier to red: + local -r exit_status_color="${bold_red?}" + fi - echo -ne "${exit_status_color}" - if [[ "${prompt_string}" -eq 1 ]]; then - # $PS1: - echo -e " +${normal} " - elif [[ "${prompt_string}" -eq 2 ]]; then - # $PS2: - echo -e " |${normal} " - else - # Default: - echo -e " ?${normal} " - fi + if [[ "${prompt_string}" -eq 1 ]]; then + # $PS1: + printf '%b +%b' "${exit_status_color}" "${normal?} " + elif [[ "${prompt_string}" -eq 2 ]]; then + # $PS2: + printf '%b |%b' "${exit_status_color}" "${normal?} " + else + # Default: + printf '%b ?%b' "${exit_status_color}" "${normal?} " + fi } function _ps1() { - local -r time='\\t' + local -r time='\t' + local -r pwd='\w' - echo -ne "${bold_white}${time} " - echo -ne "$(_user-at-host-prompt)" - echo -e "${bold_white}:${normal}${PWD}" - echo -e "$(_exit-status-prompt 1 "${exit_status}")" + printf '%b%s ' "${bold_white?}" "${time}" + _user-at-host-prompt + printf '%b:%b%s\n' "${bold_white?}" "${normal?}" "${pwd}" + _exit-status-prompt 1 "${exit_status}" } function _ps2() { - echo -e "$(_exit-status-prompt 2 "${exit_status}")" + _exit-status-prompt 2 "${exit_status}" } function prompt_command() { - # Capture the exit status of the last command: - local -r exit_status="${?}" + # Capture the exit status of the last command: + local -r exit_status="${?}" - # Build the $PS1 prompt: - PS1="$(_ps1)" + # Build the $PS1 prompt: + PS1="$(_ps1)" - # Build the $PS2 prompt: - PS2="$(_ps2)" + # Build the $PS2 prompt: + PS2="$(_ps2)" } safe_append_prompt_command prompt_command - -# vim: sw=2 ts=2 et: diff --git a/themes/githelpers.theme.bash b/themes/githelpers.theme.bash index ba089392..719effec 100644 --- a/themes/githelpers.theme.bash +++ b/themes/githelpers.theme.bash @@ -1,99 +1,106 @@ -#!/usr/bin/env bash +# shellcheck shell=bash -function _git-symbolic-ref { - git symbolic-ref -q HEAD 2> /dev/null +function _git-symbolic-ref() { + git symbolic-ref -q HEAD 2> /dev/null } # When on a branch, this is often the same as _git-commit-description, # but this can be different when two branches are pointing to the # same commit. _git-branch is used to explicitly choose the checked-out # branch. -function _git-branch { - if [[ "${SCM_GIT_GITSTATUS_RAN}" == "true" ]]; then - test -n "${VCS_STATUS_LOCAL_BRANCH}" && echo "${VCS_STATUS_LOCAL_BRANCH}" || return 1 - else - git symbolic-ref -q --short HEAD 2> /dev/null || return 1 - fi +function _git-branch() { + if [[ "${SCM_GIT_GITSTATUS_RAN:-}" == "true" ]]; then + if [[ -n "${VCS_STATUS_LOCAL_BRANCH:-}" ]]; then + echo "${VCS_STATUS_LOCAL_BRANCH}" + else + return 1 + fi + else + git symbolic-ref -q --short HEAD 2> /dev/null || return 1 + fi } -function _git-tag { - if [[ "${SCM_GIT_GITSTATUS_RAN}" == "true" ]]; then - test -n "${VCS_STATUS_TAG}" && echo "${VCS_STATUS_TAG}" - else - git describe --tags --exact-match 2> /dev/null - fi +function _git-tag() { + if [[ "${SCM_GIT_GITSTATUS_RAN:-}" == "true" ]]; then + if [[ -n "${VCS_STATUS_TAG:-}" ]]; then + echo "${VCS_STATUS_TAG}" + fi + else + git describe --tags --exact-match 2> /dev/null + fi } -function _git-commit-description { - git describe --contains --all 2> /dev/null +function _git-commit-description() { + git describe --contains --all 2> /dev/null } -function _git-short-sha { - if [[ "${SCM_GIT_GITSTATUS_RAN}" == "true" ]]; then - echo ${VCS_STATUS_COMMIT:0:7} - else - git rev-parse --short HEAD - fi +function _git-short-sha() { + if [[ "${SCM_GIT_GITSTATUS_RAN:-}" == "true" ]]; then + echo "${VCS_STATUS_COMMIT:0:7}" + else + git rev-parse --short HEAD + fi } # Try the checked-out branch first to avoid collision with branches pointing to the same ref. -function _git-friendly-ref { - if [[ "${SCM_GIT_GITSTATUS_RAN}" == "true" ]]; then - _git-branch || _git-tag || _git-short-sha # there is no tag based describe output in gitstatus - else - _git-branch || _git-tag || _git-commit-description || _git-short-sha - fi +function _git-friendly-ref() { + if [[ "${SCM_GIT_GITSTATUS_RAN:-}" == "true" ]]; then + _git-branch || _git-tag || _git-short-sha # there is no tag based describe output in gitstatus + else + _git-branch || _git-tag || _git-commit-description || _git-short-sha + fi } -function _git-num-remotes { - git remote | wc -l +function _git-num-remotes() { + git remote | wc -l } -function _git-upstream { - local ref - ref="$(_git-symbolic-ref)" || return 1 - git for-each-ref --format="%(upstream:short)" "${ref}" +function _git-upstream() { + local ref + ref="$(_git-symbolic-ref)" || return 1 + git for-each-ref --format="%(upstream:short)" "${ref}" } -function _git-upstream-remote { - local upstream - upstream="$(_git-upstream)" || return 1 +function _git-upstream-remote() { + local upstream branch + upstream="$(_git-upstream)" || return 1 - local branch - branch="$(_git-upstream-branch)" || return 1 - echo "${upstream%"/${branch}"}" + branch="$(_git-upstream-branch)" || return 1 + echo "${upstream%"/${branch}"}" } -function _git-upstream-branch { - local ref - ref="$(_git-symbolic-ref)" || return 1 +function _git-upstream-branch() { + local ref + ref="$(_git-symbolic-ref)" || return 1 - # git versions < 2.13.0 do not support "strip" for upstream format - # regex replacement gives the wrong result for any remotes with slashes in the name, - # so only use when the strip format fails. - git for-each-ref --format="%(upstream:strip=3)" "${ref}" 2> /dev/null || git for-each-ref --format="%(upstream)" "${ref}" | sed -e "s/.*\/.*\/.*\///" + # git versions < 2.13.0 do not support "strip" for upstream format + # regex replacement gives the wrong result for any remotes with slashes in the name, + # so only use when the strip format fails. + git for-each-ref --format="%(upstream:strip=3)" "${ref}" 2> /dev/null || git for-each-ref --format="%(upstream)" "${ref}" | sed -e "s/.*\/.*\/.*\///" } -function _git-upstream-behind-ahead { - git rev-list --left-right --count "$(_git-upstream)...HEAD" 2> /dev/null +function _git-upstream-behind-ahead() { + git rev-list --left-right --count "$(_git-upstream)...HEAD" 2> /dev/null } -function _git-upstream-branch-gone { - [[ "$(git status -s -b | sed -e 's/.* //')" == "[gone]" ]] +function _git-upstream-branch-gone() { + [[ "$(git status -s -b | sed -e 's/.* //')" == "[gone]" ]] } -function _git-hide-status { - [[ "$(git config --get bash-it.hide-status)" == "1" ]] +function _git-hide-status() { + [[ "$(git config --get bash-it.hide-status)" == "1" ]] } -function _git-status { - local git_status_flags= - [[ "${SCM_GIT_IGNORE_UNTRACKED}" = "true" ]] && git_status_flags='-uno' || true - git status --porcelain ${git_status_flags} 2> /dev/null +function _git-status() { + local git_status_flags= + if [[ "${SCM_GIT_IGNORE_UNTRACKED:-}" == "true" ]]; then + git_status_flags='-uno' + fi + git status --porcelain "${git_status_flags:---}" 2> /dev/null } -function _git-status-counts { - _git-status | awk ' +function _git-status-counts() { + _git-status | awk ' BEGIN { untracked=0; unstaged=0; @@ -116,85 +123,51 @@ function _git-status-counts { }' } -function _git-remote-info { +function _git-remote-info() { + local same_branch_name="" branch_prefix + # prompt handling only, reimplement because patching the routine below gets ugly + if [[ "${SCM_GIT_GITSTATUS_RAN:-}" == "true" ]]; then + [[ "${VCS_STATUS_REMOTE_NAME?}" == "" ]] && return + [[ "${VCS_STATUS_LOCAL_BRANCH?}" == "${VCS_STATUS_REMOTE_BRANCH?}" ]] && same_branch_name=true + # no multiple remote support in gitstatusd + if [[ "${SCM_GIT_SHOW_REMOTE_INFO:-}" == "true" || "${SCM_GIT_SHOW_REMOTE_INFO:-}" == "auto" ]]; then + if [[ ${same_branch_name:-} != "true" ]]; then + remote_info="${VCS_STATUS_REMOTE_NAME?}/${VCS_STATUS_REMOTE_BRANCH?}" + else + remote_info="${VCS_STATUS_REMOTE_NAME?}" + fi + elif [[ ${same_branch_name:-} != "true" ]]; then + remote_info="${VCS_STATUS_REMOTE_BRANCH?}" + fi + if [[ -n "${remote_info:-}" ]]; then + # no support for gone remote branches in gitstatusd + branch_prefix="${SCM_THEME_BRANCH_TRACK_PREFIX:-}" + echo "${branch_prefix}${remote_info:-}" + fi + else + [[ "$(_git-upstream)" == "" ]] && return - # prompt handling only, reimplement because patching the routine below gets ugly - if [[ "${SCM_GIT_GITSTATUS_RAN}" == "true" ]]; then - [[ "${VCS_STATUS_REMOTE_NAME}" == "" ]] && return - [[ "${VCS_STATUS_LOCAL_BRANCH}" == "${VCS_STATUS_REMOTE_BRANCH}" ]] && local same_branch_name=true - local same_branch_name= - [[ "${VCS_STATUS_LOCAL_BRANCH}" == "${VCS_STATUS_REMOTE_BRANCH}" ]] && same_branch_name=true - # no multiple remote support in gitstatusd - if [[ "${SCM_GIT_SHOW_REMOTE_INFO}" = "true" || "${SCM_GIT_SHOW_REMOTE_INFO}" = "auto" ]]; then - if [[ "${same_branch_name}" != "true" ]]; then - remote_info="${VCS_STATUS_REMOTE_NAME}/${VCS_STATUS_REMOTE_BRANCH}" - else - remote_info="${VCS_STATUS_REMOTE_NAME}" - fi - elif [[ ${same_branch_name} != "true" ]]; then - remote_info="${VCS_STATUS_REMOTE_BRANCH}" - fi - if [[ -n "${remote_info}" ]];then - # no support for gone remote branches in gitstatusd - local branch_prefix="${SCM_THEME_BRANCH_TRACK_PREFIX}" - echo "${branch_prefix}${remote_info}" - fi - else - [[ "$(_git-upstream)" == "" ]] && return - - [[ "$(_git-branch)" == "$(_git-upstream-branch)" ]] && local same_branch_name=true - local same_branch_name= - [[ "$(_git-branch)" == "$(_git-upstream-branch)" ]] && same_branch_name=true - if [[ ("${SCM_GIT_SHOW_REMOTE_INFO}" = "auto" && "$(_git-num-remotes)" -ge 2) || - "${SCM_GIT_SHOW_REMOTE_INFO}" = "true" ]]; then - if [[ "${same_branch_name}" != "true" ]]; then - remote_info="\$(_git-upstream)" - else - remote_info="$(_git-upstream-remote)" - fi - elif [[ ${same_branch_name} != "true" ]]; then - remote_info="\$(_git-upstream-branch)" - fi - if [[ -n "${remote_info}" ]];then - local branch_prefix - if _git-upstream-branch-gone; then - branch_prefix="${SCM_THEME_BRANCH_GONE_PREFIX}" - else - branch_prefix="${SCM_THEME_BRANCH_TRACK_PREFIX}" - fi - echo "${branch_prefix}${remote_info}" - fi - fi -} - -# Unused by bash-it, present for API compatibility -function git_status_summary { - awk ' - BEGIN { - untracked=0; - unstaged=0; - staged=0; - } - { - if (!after_first && $0 ~ /^##.+/) { - print $0 - seen_header = 1 - } else if ($0 ~ /^\?\? .+/) { - untracked += 1 - } else { - if ($0 ~ /^.[^ ] .+/) { - unstaged += 1 - } - if ($0 ~ /^[^ ]. .+/) { - staged += 1 - } - } - after_first = 1 - } - END { - if (!seen_header) { - print - } - print untracked "\t" unstaged "\t" staged - }' + [[ "$(_git-branch)" == "$(_git-upstream-branch)" ]] && same_branch_name=true + if [[ ("${SCM_GIT_SHOW_REMOTE_INFO}" == "auto" && "$(_git-num-remotes)" -ge 2) || + "${SCM_GIT_SHOW_REMOTE_INFO}" == "true" ]]; then + if [[ ${same_branch_name:-} != "true" ]]; then + # shellcheck disable=SC2016 + remote_info='$(_git-upstream)' + else + remote_info="$(_git-upstream-remote)" + fi + elif [[ ${same_branch_name:-} != "true" ]]; then + # shellcheck disable=SC2016 + remote_info='$(_git-upstream-branch)' + fi + if [[ -n "${remote_info:-}" ]]; then + local branch_prefix + if _git-upstream-branch-gone; then + branch_prefix="${SCM_THEME_BRANCH_GONE_PREFIX:-}" + else + branch_prefix="${SCM_THEME_BRANCH_TRACK_PREFIX:-}" + fi + echo "${branch_prefix}${remote_info:-}" + fi + fi } diff --git a/themes/gitline/powerline.base.bash b/themes/gitline/powerline.base.bash index 4f6b17b0..7f6f3130 100644 --- a/themes/gitline/powerline.base.bash +++ b/themes/gitline/powerline.base.bash @@ -1,5 +1,3 @@ -# Sudo check after every command -THEME_CHECK_SUDO=${THEME_CHECK_SUDO:=true} #To set color for foreground and background function set_color { diff --git a/themes/liquidprompt/liquidprompt.theme.bash b/themes/liquidprompt/liquidprompt.theme.bash old mode 100755 new mode 100644 diff --git a/themes/modern/modern.theme.bash b/themes/modern/modern.theme.bash index 3a51267c..eadb0762 100644 --- a/themes/modern/modern.theme.bash +++ b/themes/modern/modern.theme.bash @@ -1,4 +1,6 @@ # shellcheck shell=bash +# shellcheck disable=SC2034 # Expected behavior for themes. +# shellcheck disable=SC2154 #TODO: fix these all. SCM_THEME_PROMPT_PREFIX="" SCM_THEME_PROMPT_SUFFIX="" diff --git a/themes/norbu/norbu.theme.bash b/themes/norbu/norbu.theme.bash index 184c13c9..def58d81 100644 --- a/themes/norbu/norbu.theme.bash +++ b/themes/norbu/norbu.theme.bash @@ -1,21 +1,23 @@ -#!/usr/bin/env bash +# shellcheck shell=bash +# shellcheck disable=SC2034 # Expected behavior for themes. -function set_prompt_symbol () { - if test $1 -eq 0 ; then - PROMPT_SYMBOL=">_" - else - PROMPT_SYMBOL="${orange}>_${normal}" - fi +function set_prompt_symbol() { + if [[ $1 -eq 0 ]]; then + prompt_symbol=">_" + else + prompt_symbol="${orange?}>_${normal?}" + fi } -function prompt_command() { - set_prompt_symbol $? - if test -z "$VIRTUAL_ENV" ; then - PYTHON_VIRTUALENV="" - else - PYTHON_VIRTUALENV="${bold_yellow}[`basename \"$VIRTUAL_ENV\"`]" - fi - PS1="${bold_orange}${PYTHON_VIRTUALENV}${reset_color}${bold_green}[\w]${bold_blue}\[$(scm_prompt_info)\]${normal} \n${PROMPT_SYMBOL} " +function prompt_command() { + local ret_val="$?" prompt_symbol scm_prompt_info + if [[ -n "${VIRTUAL_ENV:-}" ]]; then + PYTHON_VIRTUALENV="${bold_yellow?}[${VIRTUAL_ENV##*/}]" + fi + + scm_prompt_info="$(scm_prompt_info)" + set_prompt_symbol "${ret_val}" + PS1="${bold_orange?}${PYTHON_VIRTUALENV:-}${reset_color?}${bold_green?}[\w]${bold_blue?}[${scm_prompt_info}]${normal?} \n${prompt_symbol} " } # scm themeing diff --git a/themes/nwinkler/nwinkler.theme.bash b/themes/nwinkler/nwinkler.theme.bash index f9fe4933..983c8d00 100644 --- a/themes/nwinkler/nwinkler.theme.bash +++ b/themes/nwinkler/nwinkler.theme.bash @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +# shellcheck shell=bash # Two line prompt showing the following information: # (time) SCM [username@hostname] pwd (SCM branch SCM status) @@ -29,9 +29,7 @@ prompt_setter() { else PROMPT_END=$PROMPT_END_DIRTY fi # Save history - history -a - history -c - history -r + _save-and-reload-history 1 PS1="($(clock_prompt)) $(scm_char) [${blue}\u${reset_color}@${green}\H${reset_color}] ${yellow}\w${reset_color}$(scm_prompt_info) ${reset_color}\n$(prompt_end) " PS2='> ' PS4='+ ' diff --git a/themes/nwinkler_random_colors/nwinkler_random_colors.theme.bash b/themes/nwinkler_random_colors/nwinkler_random_colors.theme.bash index f6eff908..05391b0b 100644 --- a/themes/nwinkler_random_colors/nwinkler_random_colors.theme.bash +++ b/themes/nwinkler_random_colors/nwinkler_random_colors.theme.bash @@ -1,4 +1,4 @@ -#!/bin/bash +# shellcheck shell=bash # Two line prompt showing the following information: # (time) SCM [username@hostname] pwd (SCM branch SCM status) @@ -96,9 +96,7 @@ prompt_setter() { else PROMPT_END=$PROMPT_END_DIRTY fi # Save history - history -a - history -c - history -r + _save-and-reload-history 1 PS1="($(clock_prompt)${reset_color}) $(scm_char) [${USERNAME_COLOR}\u${reset_color}@${HOSTNAME_COLOR}\H${reset_color}] ${PATH_COLOR}\w${reset_color}$(scm_prompt_info) ${reset_color}\n$(prompt_end) " PS2='> ' PS4='+ ' diff --git a/themes/oh-my-posh/oh-my-posh.theme.bash b/themes/oh-my-posh/oh-my-posh.theme.bash new file mode 100644 index 00000000..ba3c77f1 --- /dev/null +++ b/themes/oh-my-posh/oh-my-posh.theme.bash @@ -0,0 +1,8 @@ +# shellcheck shell=bash + +if _command_exists oh-my-posh; then + export POSH_THEME=${POSH_THEME:-https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/v$(oh-my-posh --version)/themes/jandedobbeleer.omp.json} + eval "$(oh-my-posh --init --shell bash --config "${POSH_THEME}")" +else + _log_warning "The oh-my-posh binary was not found on your PATH. Falling back to your existing PS1, please see the docs for more info." +fi diff --git a/themes/p4helpers.theme.bash b/themes/p4helpers.theme.bash index 27a777ac..30b520cc 100644 --- a/themes/p4helpers.theme.bash +++ b/themes/p4helpers.theme.bash @@ -1,18 +1,18 @@ -#!/usr/bin/env bash +# shellcheck shell=bash function _p4-opened { - timeout 2.0s p4 opened -s 2> /dev/null + timeout 2.0s p4 opened -s 2> /dev/null } function _p4-opened-counts { - # Return the following counts seperated by tabs: - # - count of opened files - # - count of pending changesets (other than defaults) - # - count of files in the default changeset - # - count of opened files in add mode - # - count of opened files in edit mode - # - count of opened files in delete mode - _p4-opened | awk ' + # Return the following counts seperated by tabs: + # - count of opened files + # - count of pending changesets (other than defaults) + # - count of files in the default changeset + # - count of opened files in add mode + # - count of opened files in edit mode + # - count of opened files in delete mode + _p4-opened | awk ' BEGIN { opened=0; type_array["edit"]=0; diff --git a/themes/pete/pete.theme.bash b/themes/pete/pete.theme.bash index 73fdb053..e55ad6eb 100644 --- a/themes/pete/pete.theme.bash +++ b/themes/pete/pete.theme.bash @@ -1,13 +1,16 @@ -#!/usr/bin/env bash +# shellcheck shell=bash +# shellcheck disable=SC2034 # Expected behavior for themes. -prompt_setter() { - # Save history - history -a - history -c - history -r - PS1="($(clock_prompt)) $(scm_char) [$blue\u$reset_color@$green\H$reset_color] $yellow\w${reset_color}$(scm_prompt_info)$(ruby_version_prompt) $reset_color " - PS2='> ' - PS4='+ ' +function prompt_setter() { + local clock_prompt scm_char scm_prompt_info ruby_version_prompt + clock_prompt="$(clock_prompt)" + scm_char="$(scm_char)" + scm_prompt_info="$(scm_prompt_info)" + ruby_version_prompt="$(ruby_version_prompt)" + _save-and-reload-history 1 # Save history + PS1="(${clock_prompt}) ${scm_char} [${blue?}\u${reset_color?}@${green?}\H${reset_color?}] ${yellow?}\w${reset_color?}${scm_prompt_info}${ruby_version_prompt} ${reset_color?} " + PS2='> ' + PS4='+ ' } safe_append_prompt_command prompt_setter diff --git a/themes/powerline-multiline/powerline-multiline.base.bash b/themes/powerline-multiline/powerline-multiline.base.bash index 7ae33f86..f752bd75 100644 --- a/themes/powerline-multiline/powerline-multiline.base.bash +++ b/themes/powerline-multiline/powerline-multiline.base.bash @@ -61,6 +61,8 @@ function __powerline_prompt_command { SEGMENTS_AT_RIGHT=0 LAST_SEGMENT_COLOR="" + _save-and-reload-history "${HISTORY_AUTOSAVE:-0}" + ## left prompt ## for segment in $POWERLINE_LEFT_PROMPT; do local info="$(__powerline_${segment}_prompt)" diff --git a/themes/powerline-naked/powerline-naked.base.bash b/themes/powerline-naked/powerline-naked.base.bash index 16b633be..dfc63f76 100644 --- a/themes/powerline-naked/powerline-naked.base.bash +++ b/themes/powerline-naked/powerline-naked.base.bash @@ -26,6 +26,8 @@ function __powerline_left_segment { LEFT_PROMPT+="$(set_color ${params[1]} -)${pad_before_segment}${params[0]}${normal}" LAST_SEGMENT_COLOR=${params[1]} (( SEGMENTS_AT_LEFT += 1 )) + + _save-and-reload-history "${HISTORY_AUTOSAVE:-0}" } function __powerline_left_last_segment_padding { diff --git a/themes/powerline/powerline.base.bash b/themes/powerline/powerline.base.bash index 98bcd82c..84469e87 100644 --- a/themes/powerline/powerline.base.bash +++ b/themes/powerline/powerline.base.bash @@ -1,13 +1,12 @@ # shellcheck shell=bash -# Define this here so it can be used by all of the Powerline themes -THEME_CHECK_SUDO=${THEME_CHECK_SUDO:=true} +# shellcheck disable=SC2034 # Expected behavior for themes. function set_color() { - set +u - if [[ "${1}" != "-" ]]; then + local fg='' bg='' + if [[ "${1:-}" != "-" ]]; then fg="38;5;${1}" fi - if [[ "${2}" != "-" ]]; then + if [[ "${2:-}" != "-" ]]; then bg="48;5;${2}" [[ -n "${fg}" ]] && bg=";${bg}" fi @@ -96,14 +95,13 @@ function __powerline_k8s_namespace_prompt() { } function __powerline_python_venv_prompt() { - set +u local python_venv="" - if [[ -n "${CONDA_DEFAULT_ENV}" ]]; then + if [[ -n "${CONDA_DEFAULT_ENV:-}" ]]; then python_venv="${CONDA_DEFAULT_ENV}" PYTHON_VENV_CHAR=${CONDA_PYTHON_VENV_CHAR} - elif [[ -n "${VIRTUAL_ENV}" ]]; then - python_venv=$(basename "${VIRTUAL_ENV}") + elif [[ -n "${VIRTUAL_ENV:-}" ]]; then + python_venv="${VIRTUAL_ENV##*/}" fi [[ -n "${python_venv}" ]] && echo "${PYTHON_VENV_CHAR}${python_venv}|${PYTHON_VENV_THEME_PROMPT_COLOR}" @@ -134,14 +132,12 @@ function __powerline_scm_prompt() { elif [[ "${SCM_SVN_CHAR}" == "${SCM_CHAR}" ]]; then scm_prompt+="${SCM_CHAR}${SCM_BRANCH}${SCM_STATE}" fi - echo "$(eval "echo ${scm_prompt}")${scm}|${color}" + echo "${scm_prompt?}|${color}" fi } function __powerline_cwd_prompt() { - local cwd=$(pwd | sed "s|^${HOME}|~|") - - echo "${cwd}|${CWD_THEME_PROMPT_COLOR}" + echo "\w|${CWD_THEME_PROMPT_COLOR}" } function __powerline_hostname_prompt() { @@ -157,10 +153,10 @@ function __powerline_clock_prompt() { } function __powerline_battery_prompt() { - local color="" - local battery_status="$(battery_percentage 2> /dev/null)" + local color="" battery_status + battery_status="$(battery_percentage 2> /dev/null)" - if [[ -z "${battery_status}" ]] || [[ "${battery_status}" = "-1" ]] || [[ "${battery_status}" = "no" ]]; then + if [[ -z "${battery_status}" || "${battery_status}" == "-1" || "${battery_status}" == "no" ]]; then true else if [[ "$((10#${battery_status}))" -le 5 ]]; then @@ -176,7 +172,7 @@ function __powerline_battery_prompt() { } function __powerline_in_vim_prompt() { - if [ -n "$VIMRUNTIME" ]; then + if [[ -n "$VIMRUNTIME" ]]; then echo "${IN_VIM_THEME_PROMPT_TEXT}|${IN_VIM_THEME_PROMPT_COLOR}" fi } @@ -221,7 +217,8 @@ function __powerline_command_number_prompt() { } function __powerline_duration_prompt() { - local duration=$(_command_duration) + local duration + duration=$(_command_duration) [[ -n "$duration" ]] && echo "${duration}|${COMMAND_DURATION_PROMPT_COLOR}" } @@ -241,12 +238,12 @@ function __powerline_left_segment() { # Since the previous segment wasn't the last segment, add padding, if needed # if [[ "${POWERLINE_COMPACT_BEFORE_SEPARATOR}" -eq 0 ]]; then - LEFT_PROMPT+="$(set_color - "${LAST_SEGMENT_COLOR}") ${normal}" + LEFT_PROMPT+="$(set_color - "${LAST_SEGMENT_COLOR}") ${normal?}" fi if [[ "${LAST_SEGMENT_COLOR}" -eq "${params[1]}" ]]; then - LEFT_PROMPT+="$(set_color - "${LAST_SEGMENT_COLOR}")${POWERLINE_LEFT_SEPARATOR_SOFT}${normal}" + LEFT_PROMPT+="$(set_color - "${LAST_SEGMENT_COLOR}")${POWERLINE_LEFT_SEPARATOR_SOFT}${normal?}" else - LEFT_PROMPT+="$(set_color "${LAST_SEGMENT_COLOR}" "${params[1]}")${POWERLINE_LEFT_SEPARATOR}${normal}" + LEFT_PROMPT+="$(set_color "${LAST_SEGMENT_COLOR}" "${params[1]}")${POWERLINE_LEFT_SEPARATOR}${normal?}" fi fi @@ -256,7 +253,7 @@ function __powerline_left_segment() { } function __powerline_left_last_segment_padding() { - LEFT_PROMPT+="$(set_color - "${LAST_SEGMENT_COLOR}") ${normal}" + LEFT_PROMPT+="$(set_color - "${LAST_SEGMENT_COLOR}") ${normal?}" } function __powerline_last_status_prompt() { @@ -265,38 +262,40 @@ function __powerline_last_status_prompt() { function __powerline_prompt_command() { local last_status="$?" ## always the first - local separator_char="${POWERLINE_PROMPT_CHAR}" + local separator_char="${POWERLINE_PROMPT_CHAR}" info prompt_color LEFT_PROMPT="" SEGMENTS_AT_LEFT=0 LAST_SEGMENT_COLOR="" + _save-and-reload-history "${HISTORY_AUTOSAVE:-0}" + if [[ -n "${POWERLINE_PROMPT_DISTRO_LOGO}" ]]; then LEFT_PROMPT+="$(set_color "${PROMPT_DISTRO_LOGO_COLOR}" "${PROMPT_DISTRO_LOGO_COLORBG}")${PROMPT_DISTRO_LOGO}$(set_color - -)" fi ## left prompt ## for segment in $POWERLINE_PROMPT; do - local info="$(__powerline_"${segment}"_prompt)" + info="$(__powerline_"${segment}"_prompt)" [[ -n "${info}" ]] && __powerline_left_segment "${info}" done - [[ "${last_status}" -ne 0 ]] && __powerline_left_segment "$(__powerline_last_status_prompt ${last_status})" + [[ "${last_status}" -ne 0 ]] && __powerline_left_segment "$(__powerline_last_status_prompt "${last_status}")" - if [[ -n "${LEFT_PROMPT}" ]] && [[ "${POWERLINE_COMPACT_AFTER_LAST_SEGMENT}" -eq 0 ]]; then + if [[ -n "${LEFT_PROMPT}" ]] && [[ "${POWERLINE_COMPACT_AFTER_LAST_SEGMENT:-}" -eq 0 ]]; then __powerline_left_last_segment_padding fi # By default we try to match the prompt to the adjacent segment's background color, # but when part of the prompt exists within that segment, we instead match the foreground color. - local prompt_color="$(set_color "${LAST_SEGMENT_COLOR}" -)" + prompt_color="$(set_color "${LAST_SEGMENT_COLOR}" -)" if [[ -n "${LEFT_PROMPT}" ]] && [[ -n "${POWERLINE_LEFT_LAST_SEGMENT_PROMPT_CHAR}" ]]; then LEFT_PROMPT+="$(set_color - "${LAST_SEGMENT_COLOR}")${POWERLINE_LEFT_LAST_SEGMENT_PROMPT_CHAR}" - prompt_color="${normal}" + prompt_color="${normal?}" fi - [[ -n "${LEFT_PROMPT}" ]] && LEFT_PROMPT+="${prompt_color}${separator_char}${normal}" + [[ -n "${LEFT_PROMPT}" ]] && LEFT_PROMPT+="${prompt_color}${separator_char}${normal?}" - if [[ "${POWERLINE_COMPACT_PROMPT}" -eq 0 ]]; then + if [[ "${POWERLINE_COMPACT_PROMPT:-}" -eq 0 ]]; then LEFT_PROMPT+=" " fi diff --git a/themes/powerline/powerline.theme.bash b/themes/powerline/powerline.theme.bash index d7ac77ed..49b397aa 100644 --- a/themes/powerline/powerline.theme.bash +++ b/themes/powerline/powerline.theme.bash @@ -1,4 +1,5 @@ # shellcheck shell=bash +# shellcheck disable=SC2034 # Expected behavior for themes. # shellcheck source=../../themes/powerline/powerline.base.bash . "$BASH_IT/themes/powerline/powerline.base.bash" diff --git a/themes/powerturk/powerturk.theme.bash b/themes/powerturk/powerturk.theme.bash index 4590a8aa..3352f680 100644 --- a/themes/powerturk/powerturk.theme.bash +++ b/themes/powerturk/powerturk.theme.bash @@ -43,7 +43,7 @@ _swd(){ begin="" # The unshortened beginning of the path. shortbegin="" # The shortened beginning of the path. current="" # The section of the path we're currently working on. - end="${2:-$(pwd)}/" # The unmodified rest of the path. + end="${2:-${PWD}}/" # The unmodified rest of the path. if [[ "$end" =~ "$HOME" ]]; then INHOME=1 diff --git a/themes/pure/pure.theme.bash b/themes/pure/pure.theme.bash index 3672a72c..4dd59e02 100644 --- a/themes/pure/pure.theme.bash +++ b/themes/pure/pure.theme.bash @@ -1,39 +1,37 @@ # shellcheck shell=bash +# shellcheck disable=SC2034 # Expected behavior for themes. # scm theming SCM_THEME_PROMPT_PREFIX="|" SCM_THEME_PROMPT_SUFFIX="" -SCM_THEME_PROMPT_DIRTY=" ${bold_red}✗${normal}" -SCM_THEME_PROMPT_CLEAN=" ${green}✓${normal}" -SCM_GIT_CHAR="${green}±${normal}" -SCM_SVN_CHAR="${bold_cyan}⑆${normal}" -SCM_HG_CHAR="${bold_red}☿${normal}" +SCM_THEME_PROMPT_DIRTY=" ${bold_red?}✗${normal?}" +SCM_THEME_PROMPT_CLEAN=" ${green?}✓${normal?}" +SCM_GIT_CHAR="${green?}±${normal?}" +SCM_SVN_CHAR="${bold_cyan?}⑆${normal?}" +SCM_HG_CHAR="${bold_red?}☿${normal?}" VIRTUALENV_THEME_PROMPT_PREFIX="(" VIRTUALENV_THEME_PROMPT_SUFFIX=")" -### TODO: openSUSE has already colors enabled, check if those differs from stock -# LS colors, made with http://geoff.greer.fm/lscolors/ -# export LSCOLORS="Gxfxcxdxbxegedabagacad" -# export LS_COLORS='no=00:fi=00:di=01;34:ln=00;36:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=41;33;01:ex=00;32:*.cmd=00;32:*.exe=01;32:*.com=01;32:*.bat=01;32:*.btm=01;32:*.dll=01;32:*.tar=00;31:*.tbz=00;31:*.tgz=00;31:*.rpm=00;31:*.deb=00;31:*.arj=00;31:*.taz=00;31:*.lzh=00;31:*.lzma=00;31:*.zip=00;31:*.zoo=00;31:*.z=00;31:*.Z=00;31:*.gz=00;31:*.bz2=00;31:*.tb2=00;31:*.tz2=00;31:*.tbz2=00;31:*.avi=01;35:*.bmp=01;35:*.fli=01;35:*.gif=01;35:*.jpg=01;35:*.jpeg=01;35:*.mng=01;35:*.mov=01;35:*.mpg=01;35:*.pcx=01;35:*.pbm=01;35:*.pgm=01;35:*.png=01;35:*.ppm=01;35:*.tga=01;35:*.tif=01;35:*.xbm=01;35:*.xpm=01;35:*.dl=01;35:*.gl=01;35:*.wmv=01;35:*.aiff=00;32:*.au=00;32:*.mid=00;32:*.mp3=00;32:*.ogg=00;32:*.voc=00;32:*.wav=00;32:' - -pure_prompt() { - ps_host="${bold_blue}\h${normal}" - ps_user="${green}\u${normal}" - ps_user_mark="${green} $ ${normal}" - ps_root="${red}\u${red}" - ps_root_mark="${red} # ${normal}" - ps_path="${yellow}\w${normal}" +function pure_prompt() { + local ps_host="${bold_blue?}\h${normal?}" + local ps_user="${green?}\u${normal?}" + local ps_user_mark="${green?} \$ ${normal?}" + local ps_root="${red?}\u${red?}" + local ps_root_mark="${red?} \$ ${normal?}" + local ps_path="${yellow?}\w${normal?}" + local virtualenv_prompt scm_prompt + virtualenv_prompt="$(virtualenv_prompt)" + scm_prompt="$(scm_prompt)" # make it work - case $(id -u) in + case "${EUID:-$UID}" in 0) - PS1="$(virtualenv_prompt)$ps_root@$ps_host$(scm_prompt):$ps_path$ps_root_mark" - ;; - *) - PS1="$(virtualenv_prompt)$ps_user@$ps_host$(scm_prompt):$ps_path$ps_user_mark" + ps_user_mark="${ps_root_mark}" + ps_user="${ps_root}" ;; esac + PS1="${virtualenv_prompt}${ps_user}@${ps_host}${scm_prompt}:${ps_path}${ps_user_mark}" } safe_append_prompt_command pure_prompt diff --git a/themes/purity/purity.theme.bash b/themes/purity/purity.theme.bash index 8fc03bf3..0fc6c4cf 100644 --- a/themes/purity/purity.theme.bash +++ b/themes/purity/purity.theme.bash @@ -1,34 +1,44 @@ # shellcheck shell=bash +# shellcheck disable=SC2034 # Expected behavior for themes. -SCM_THEME_PROMPT_DIRTY=" ${bold_red}⊘${normal}" -SCM_THEME_PROMPT_CLEAN=" ${bold_green}✓${normal}" -SCM_THEME_PROMPT_PREFIX="${reset_color}( " -SCM_THEME_PROMPT_SUFFIX=" ${reset_color})" +SCM_THEME_PROMPT_DIRTY=" ${bold_red?}⊘${normal?}" +SCM_THEME_PROMPT_CLEAN=" ${bold_green?}✓${normal?}" +SCM_THEME_PROMPT_PREFIX="${reset_color?}( " +SCM_THEME_PROMPT_SUFFIX=" ${reset_color?})" -GIT_THEME_PROMPT_DIRTY=" ${bold_red}⊘${normal}" -GIT_THEME_PROMPT_CLEAN=" ${bold_green}✓${normal}" -GIT_THEME_PROMPT_PREFIX="${reset_color}( " -GIT_THEME_PROMPT_SUFFIX=" ${reset_color})" +GIT_THEME_PROMPT_DIRTY=" ${bold_red?}⊘${normal?}" +GIT_THEME_PROMPT_CLEAN=" ${bold_green?}✓${normal?}" +GIT_THEME_PROMPT_PREFIX="${reset_color?}( " +GIT_THEME_PROMPT_SUFFIX=" ${reset_color?})" -STATUS_THEME_PROMPT_BAD="${bold_red}❯${reset_color}${normal} " -STATUS_THEME_PROMPT_OK="${bold_green}❯${reset_color}${normal} " -PURITY_THEME_PROMPT_COLOR="${PURITY_THEME_PROMPT_COLOR:=$blue}" +STATUS_THEME_PROMPT_BAD="${bold_red?}❯${reset_color?}${normal?} " +STATUS_THEME_PROMPT_OK="${bold_green?}❯${reset_color?}${normal?} " +: "${PURITY_THEME_PROMPT_COLOR:=$blue}" -venv_prompt() { +function venv_prompt() { python_venv="" # Detect python venv - if [[ -n "${CONDA_DEFAULT_ENV}" ]]; then - python_venv="($PYTHON_VENV_CHAR${CONDA_DEFAULT_ENV}) " + if [[ -n "${CONDA_DEFAULT_ENV:-}" ]]; then + python_venv="(${PYTHON_VENV_CHAR}${CONDA_DEFAULT_ENV}) " elif [[ -n "${VIRTUAL_ENV}" ]]; then - python_venv="($PYTHON_VENV_CHAR$(basename "${VIRTUAL_ENV}")) " + python_venv="(${PYTHON_VENV_CHAR}${VIRTUAL_ENV##*/}) " fi [[ -n "${python_venv}" ]] && echo "${python_venv}" } function prompt_command() { - retval=$? - local ret_status="$([ $retval -eq 0 ] && echo -e "$STATUS_THEME_PROMPT_OK" || echo -e "$STATUS_THEME_PROMPT_BAD")" - PS1="\n${PURITY_THEME_PROMPT_COLOR}\w $(scm_prompt_info)\n${ret_status}$(venv_prompt)" + local retval="$?" ret_status python_venv scm_prompt_info venv_prompt + case "${retval}" in + 0) + ret_status="$STATUS_THEME_PROMPT_OK" + ;; + *) + ret_status="$STATUS_THEME_PROMPT_BAD" + ;; + esac + scm_prompt_info="$(scm_prompt_info)" + venv_prompt="$(venv_prompt)" + PS1="\n${PURITY_THEME_PROMPT_COLOR}\w ${scm_prompt_info}\n${ret_status}${venv_prompt}" } safe_append_prompt_command prompt_command diff --git a/themes/rainbowbrite/rainbowbrite.theme.bash b/themes/rainbowbrite/rainbowbrite.theme.bash index 63c64b72..07e5843f 100644 --- a/themes/rainbowbrite/rainbowbrite.theme.bash +++ b/themes/rainbowbrite/rainbowbrite.theme.bash @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +# shellcheck shell=bash # based off of n0qorg # looks like, if you're in a git repo: @@ -7,9 +7,7 @@ prompt_setter() { # Save history - history -a - history -c - history -r + _save-and-reload-history 1 # displays user@server in purple # PS1="$red$(scm_char) $purple\u@\h$reset_color:$blue\w$yellow$(scm_prompt_info)$(ruby_version_prompt) $black\$$reset_color " # no user@server diff --git a/themes/rana/rana.theme.bash b/themes/rana/rana.theme.bash old mode 100755 new mode 100644 diff --git a/vendor/github.com/erichs/composure/composure.sh b/vendor/github.com/erichs/composure/composure.sh index 0c21fe08..ae11f9b3 100755 --- a/vendor/github.com/erichs/composure/composure.sh +++ b/vendor/github.com/erichs/composure/composure.sh @@ -19,7 +19,7 @@ _bootstrap_composure() { _get_composure_dir () { - if [ -n "$XDG_DATA_HOME" ]; then + if [ -n "${XDG_DATA_HOME:-}" ]; then echo "$XDG_DATA_HOME/composure" else echo "$HOME/.local/composure" @@ -30,7 +30,7 @@ _get_author_name () { typeset name localname localname="$(git --git-dir "$(_get_composure_dir)/.git" config --get user.name)" - for name in "$GIT_AUTHOR_NAME" "$localname"; do + for name in "${GIT_AUTHOR_NAME:-}" "$localname"; do if [ -n "$name" ]; then echo "$name" break @@ -55,7 +55,7 @@ _letterpress () } _determine_printf_cmd() { - if [ -z "$_printf_cmd" ]; then + if [ -z "${_printf_cmd:-}" ]; then _printf_cmd=printf # prefer GNU gprintf if available [ -x "$(which gprintf 2>/dev/null)" ] && _printf_cmd=gprintf @@ -78,7 +78,7 @@ _longest_function_name_length () _temp_filename_for () { - typeset file=$(mktemp "/tmp/$1.XXXX") + typeset file="$(mktemp "/tmp/$1.XXXX")" command rm "$file" 2>/dev/null # ensure file is unlinked prior to use echo "$file" } @@ -136,7 +136,7 @@ _transcribe () if [ -d "$composure_dir" ]; then _add_composure_file "$func" "$file" "$operation" "$comment" else - if [ "$USE_COMPOSURE_REPO" = "0" ]; then + if [ "${USE_COMPOSURE_REPO:-}" = "0" ]; then return # if you say so... fi printf "%s\n" "I see you don't have a $composure_dir repo..." @@ -223,7 +223,7 @@ _load_composed_functions () { # you may disable this by adding the following line to your shell startup: # export LOAD_COMPOSED_FUNCTIONS=0 - if [ "$LOAD_COMPOSED_FUNCTIONS" = "0" ]; then + if [ "${LOAD_COMPOSED_FUNCTIONS:-}" = "0" ]; then return # if you say so... fi @@ -379,7 +379,7 @@ metafor () # 'grep' for the metadata keyword, and then parse/filter the matching line # grep keyword # strip trailing '|"|; # ignore thru keyword and leading '|" - sed -n "/$keyword / s/['\";]*\$//;s/^[ ]*$keyword ['\"]*\([^([].*\)*\$/\1/p" + sed -n "/$keyword / s/['\";]*\$//;s/^[ ]*\(: _\)*$keyword ['\"]*\([^([].*\)*\$/\2/p" } reference () @@ -462,7 +462,7 @@ revise () cat "$composure_dir/$func.inc" > "$temp" fi - if [ -z "$EDITOR" ] + if [ -z "${EDITOR:-}" ] then typeset EDITOR=vi fi diff --git a/vendor/github.com/gaelicWizard/bash-progcomp/.editorconfig b/vendor/github.com/gaelicWizard/bash-progcomp/.editorconfig new file mode 100644 index 00000000..e1eba2cf --- /dev/null +++ b/vendor/github.com/gaelicWizard/bash-progcomp/.editorconfig @@ -0,0 +1,40 @@ +# EditorConfig is awesome: http://EditorConfig.org + +[*] +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[**.md] +indent_style = space +indent_size = 2 +trim_trailing_whitespace = false + +[.git*] +indent_style = tab + +[**.*sh] +indent_style = tab +indent_size = tab + +shell_variant = bash +binary_next_line = true # like -bn +switch_case_indent = false # like -ci +space_redirects = true # like -sr +keep_padding = false # like -kp +function_next_line = true # like -fn +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[**.bats] +indent_style = tab +indent_size = tab + +shell_variant = bash +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true diff --git a/vendor/github.com/gaelicWizard/bash-progcomp/LICENSE b/vendor/github.com/gaelicWizard/bash-progcomp/LICENSE new file mode 100644 index 00000000..cd48959a --- /dev/null +++ b/vendor/github.com/gaelicWizard/bash-progcomp/LICENSE @@ -0,0 +1,30 @@ +BSD 3-Clause License + +Copyright (c) 2006, Playhaus +Copyright (c) 2021, gaelicWizard.LLC +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/gaelicWizard/bash-progcomp/defaults.completion.bash b/vendor/github.com/gaelicWizard/bash-progcomp/defaults.completion.bash new file mode 100644 index 00000000..67cb4fe5 --- /dev/null +++ b/vendor/github.com/gaelicWizard/bash-progcomp/defaults.completion.bash @@ -0,0 +1,286 @@ +# shellcheck shell=bash +# shellcheck disable=SC2207 +# +# Bash command line completion for defaults +# +# Version 1.0 created by Jonathon Mah on 2006-11-08. +# Version 2.0 written by John Pell on 2021-09-11. +# + +function matchpattern() +{ + local PATTERN=${2:?$FUNCNAME: a pattern is required} + local SEP=${3:-|} + [[ -z "${PATTERN##*${SEP}${1}${SEP}*}" ]] +} + +function _defaults_verbs() +{ + local IFS=$'\n' # Treat only newlines as delimiters in string operations. + local LC_CTYPE='C' # Do not consider character set in string operations. + local LC_COLLATE='C' # Do not consider character set in pattern matching. + local cur="${COMP_WORDS[COMP_CWORD]}" + local prev="${COMP_WORDS[COMP_CWORD - 1]}" + COMPREPLY=() + + case $COMP_CWORD in + 1) + candidates=("${cmds// /$IFS}" "${host_opts[@]}") + ;; + 2 | 3) + candidates=("${cmds// /$IFS}") + ;; + *) + return 1 + ;; + esac + + COMPREPLY=($(compgen -W "${candidates[*]}" | grep -i "^${cur}")) + return 0 +} + +function _defaults_domains() +{ + local IFS=$'\n' # Treat only newlines as delimiters in string operations. + local LC_CTYPE='C' # Do not consider character set in string operations. + local LC_COLLATE='C' # Do not consider character set in pattern matching. + local cur="${COMP_WORDS[COMP_CWORD]}" + local prev="${COMP_WORDS[COMP_CWORD - 1]}" + COMPREPLY=() + + if [[ "$BASH_VERSINFO" -ge 4 ]] + then # Exponential performance issue on strings greater than about 10k. + local domains="$(defaults domains)" + local candidates=($(compgen -W "${domains//, /$IFS}" | grep -i "^${cur}")) + else + local domains="$(defaults domains | sed -e 's/, /^/g' | tr '^' '\n')" + local candidates=($(compgen -W "${domains}" | grep -i "^${cur}")) + fi + COMPREPLY=($(printf '%q\n' "${candidates[@]}")) + if grep -q "^$cur" <<< '-app' + then + COMPREPLY[${#COMPREPLY[@]}]="-app" + elif grep -q "^$cur" <<< '-g' + then + COMPREPLY[${#COMPREPLY[@]}]="-g" + fi + + return 0 +} + +function _defaults() +{ + local IFS=$'\n' # Treat only newlines as delimiters in string operations. + local LC_CTYPE='C' # Do not consider character set in string operations. + local LC_COLLATE='C' # Do not consider character set in pattern matching. + local cur="${COMP_WORDS[COMP_CWORD]}" + local prev="${COMP_WORDS[COMP_CWORD - 1]}" + COMPREPLY=() + + local host_opts cmds cmd domain keys key_index candidates verbs value_types + + host_opts=('-currentHost' '-host') + cmds=' delete domains export find help import read read-type rename write ' + value_types=('-string' '-data' '-integer' '-float' '-boolean' '-date' '-array' '-array-add' '-dict' '-dict-add') + + case $COMP_CWORD in + 1) + _defaults_verbs + return "$?" + ;; + 2) + case $prev in + "-currentHost") + _defaults_verbs + ;; + "-host") + _known_hosts -a + ;; + *) + if matchpattern "$prev" "${cmds// /|}" + then + # TODO: not correct for verbs: domains, find, help + _defaults_domains + else + return 1 # verb is not recognized + fi + ;; + esac + return "$?" + ;; + 3) + case ${COMP_WORDS[1]} in + "-currentHost") + _defaults_domains + return "$?" + ;; + "-host") + _defaults_verbs + return "$?" + ;; + esac + ;; + 4) + case ${COMP_WORDS[1]} in + "-host") + if matchpattern "$prev" "${cmds// /|}" + then + # TODO: not correct for verbs: domains, find, help + _defaults_domains + else + return 1 # verb is not recognized + fi + ;; + esac + ;; + esac + + # Both a domain and command have been specified + + case ${COMP_WORDS[1]} in + "-currentHost") + if matchpattern "${COMP_WORDS[2]}" "${cmds// /|}" + then + cmd="${COMP_WORDS[2]}" + domain="${COMP_WORDS[3]}" + key_index=4 + if [[ "$domain" == "-app" ]] + then + if [[ $COMP_CWORD -eq 4 ]] + then + # Completing application name. Can't help here, sorry + return 0 + fi + domain="-app ${COMP_WORDS[4]}" + key_index=5 + fi + fi + ;; + "-host") + if matchpattern "${COMP_WORDS[3]}" "${cmds// /|}" + then + cmd="${COMP_WORDS[3]}" + domain="${COMP_WORDS[4]}" + key_index=5 + if [[ "$domain" == "-app" ]] + then + if [[ $COMP_CWORD -eq 5 ]] + then + # Completing application name. Can't help here, sorry + return 0 + fi + domain="-app ${COMP_WORDS[5]}" + key_index=6 + fi + fi + ;; + *) + if matchpattern "${COMP_WORDS[1]}" "${cmds// /|}" + then + cmd="${COMP_WORDS[1]}" + domain="${COMP_WORDS[2]}" + key_index=3 + if [[ "$domain" == "-app" ]] + then + if [[ $COMP_CWORD -eq 3 ]] + then + # Completing application name. Can't help here, sorry + return 0 + fi + domain="-app ${COMP_WORDS[3]}" + key_index=4 + fi + fi + ;; + + esac + + keys=($(defaults read "$domain" 2> /dev/null | sed -n -e '/^ [^}) ]/p' | sed -e 's/^ \([^" ]\{1,\}\) = .*$/\1/g' -e 's/^ "\([^"]\{1,\}\)" = .*$/\1/g')) + + case $cmd in + read | read-type) + # Complete key + if candidates=($(compgen -W "${keys[*]:-}" | grep -i "^${cur}")) + then + COMPREPLY=($(printf '%q\n' "${candidates[@]}")) + fi + ;; + write) + if [[ $key_index -eq $COMP_CWORD ]] + then + # Complete key + if candidates=($(compgen -W "${keys[*]:-}" | grep -i "^${cur}")) + then + COMPREPLY=($(printf '%q\n' "${candidates[@]}")) + fi + elif [[ $((key_index + 1)) -eq $COMP_CWORD ]] + then + # Complete value type + local cur_type="$(defaults read-type "$domain" "${COMP_WORDS[key_index]}" 2> /dev/null | sed -e 's/^Type is \(.*\)/-\1/' -e's/dictionary/dict/' | grep "^$cur")" + if [[ $cur_type ]] + then + COMPREPLY=("$cur_type") + else + COMPREPLY=($(compgen -W "${value_types[*]}" -- "$cur")) + fi + elif [[ $((key_index + 2)) -eq $COMP_CWORD ]] + then + # Complete value + COMPREPLY=($(defaults read "$domain" "${COMP_WORDS[key_index]}" 2> /dev/null | grep -i "^${cur//\\/\\\\}")) + fi + ;; + rename) + if [[ $key_index -eq $COMP_CWORD || $((key_index + 1)) -eq $COMP_CWORD ]] + then + # Complete source and destination keys + if candidates=($(compgen -W "${keys[*]:-}" | grep -i "^${cur}")) + then + COMPREPLY=($(printf '%q\n' "${candidates[@]}")) + fi + fi + ;; + delete) + if [[ $key_index -eq $COMP_CWORD ]] + then + # Complete key + if candidates=($(compgen -W "${keys[*]:-}" | grep -i "^${cur}")) + then + COMPREPLY=($(printf '%q\n' "${candidates[@]}")) + fi + fi + ;; + esac + + return 0 +} + +complete -F _defaults -o default defaults + +# This file is licensed under the BSD license, as follows: +# +# Copyright (c) 2006, Playhaus +# Copyright (c) 2021, gaelicWizard.LLC +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the names of the authors nor the names of its contributors may be +# used to endorse or promote products derived from this software without +# specific prior written permission. +# +# This software is provided by the copyright holders and contributors "as is" +# and any express or implied warranties, including, but not limited to, the +# implied warranties of merchantability and fitness for a particular purpose are +# disclaimed. In no event shall the copyright owner or contributors be liable +# for any direct, indirect, incidental, special, exemplary, or consequential +# damages (including, but not limited to, procurement of substitute goods or +# services; loss of use, data, or profits; or business interruption) however +# caused and on any theory of liability, whether in contract, strict liability, +# or tort (including negligence or otherwise) arising in any way out of the use +# of this software, even if advised of the possibility of such damage. diff --git a/vendor/github.com/gaelicWizard/bash-progcomp/defaults.completion.bats b/vendor/github.com/gaelicWizard/bash-progcomp/defaults.completion.bats new file mode 100755 index 00000000..65b32dad --- /dev/null +++ b/vendor/github.com/gaelicWizard/bash-progcomp/defaults.completion.bats @@ -0,0 +1,184 @@ +#!/usr/bin/env bats + +load ../test_helper + +function local_setup() { + load ../../completion/available/defaults.completion + function _known_hosts() { :; } + function defaults() { echo 'NSGlobalDomain, Bash It'; } +} + +function __check_completion() { + # Get the parameters as a single value + COMP_LINE=$* + + # Get the parameters as an array + eval set -- "$@" + COMP_WORDS=("$@") + + # Index of the cursor in the line + COMP_POINT=${#COMP_LINE} + + # Get the last character of the line that was entered + COMP_LAST=$((${COMP_POINT} - 1)) + + # If the last character was a space... + if [[ ${COMP_LINE:$COMP_LAST} = ' ' ]]; then + # ...then add an empty array item + COMP_WORDS+=('') + fi + + # Word index of the last word + COMP_CWORD=$(( ${#COMP_WORDS[@]} - 1 )) + + # Run the Bash-it completion function + _defaults + + # Return the completion output + echo "${COMPREPLY[@]}" +} + +@test "completion defaults: ensure that the _defaults function is available" { + type -a _defaults &> /dev/null + assert_success +} + +@test "completion defaults: - show verbs and options" { + run __check_completion 'defaults ' + assert_line -n 0 'delete domains export find help import read read-type rename write -currentHost -host' +} + +@test "completion defaults: r* - show matching verbs" { + run __check_completion 'defaults r' + assert_line -n 0 'read read-type rename' +} + +@test "completion defaults: R* - show matching verbs" { + run __check_completion 'defaults R' + assert_line -n 0 'read read-type rename' +} + +@test "completion defaults: -* - show matching flags" { + run __check_completion 'defaults -' + assert_line -n 0 '-currentHost -host' +} + +@test "completion defaults: -currentHost - show verbs" { + run __check_completion 'defaults -currentHost ' + assert_line -n 0 'delete domains export find help import read read-type rename write' +} + +@test "completion defaults: -host - show nothing" { + run __check_completion 'defaults -host ' + assert_line -n 0 "$(_known_hosts -a)" +} + +@test "completion defaults: -host some_computer_name - show verbs" { + run __check_completion 'defaults -host some_computer_name ' + assert_line -n 0 'delete domains export find help import read read-type rename write' +} + +@test "completion defaults: read - show all domains" { + run __check_completion 'defaults read ' + assert_line -n 0 "NSGlobalDomain Bash\ It -app" +} + +@test "completion defaults: read nsg* - show matching domains" { + run __check_completion 'defaults read nsg' + assert_line -n 0 "NSGlobalDomain" +} + +@test "completion defaults: read NSG* - show matching domains" { + run __check_completion 'defaults read NSG' + assert_line -n 0 "NSGlobalDomain" +} + +@test "completion defaults: read bash* - show matching domains" { + run __check_completion 'defaults read bash' + assert_line -n 0 "Bash\ It" +} + +@test "completion defaults: read BASH* - show matching domains" { + run __check_completion 'defaults read BASH' + assert_line -n 0 "Bash\ It" +} + +@test "completion defaults: read bash* - show matching domains (with spaces)" { + run __check_completion 'defaults read bash\ i' + assert_line -n 0 "Bash\ It" +} + +@test "completion defaults: read BASH* - show matching domains (with spaces)" { + run __check_completion 'defaults read BASH\ I' + assert_line -n 0 "Bash\ It" +} + +@test "completion defaults: -currentHost read - show all domains" { + run __check_completion 'defaults -currentHost read ' + assert_line -n 0 "NSGlobalDomain Bash\ It -app" +} + +@test "completion defaults: -currentHost read nsg* - show matching domains" { + run __check_completion 'defaults -currentHost read nsg' + assert_line -n 0 "NSGlobalDomain" +} + +@test "completion defaults: -currentHost read NSG* - show matching domains" { + run __check_completion 'defaults -currentHost read NSG' + assert_line -n 0 "NSGlobalDomain" +} + +@test "completion defaults: -currentHost read bash* - show matching domains" { + run __check_completion 'defaults -currentHost read bash' + assert_line -n 0 "Bash\ It" +} + +@test "completion defaults: -currentHost read BASH* - show matching domains" { + run __check_completion 'defaults -currentHost read BASH' + assert_line -n 0 "Bash\ It" +} + +@test "completion defaults: -currentHost read bash* - show matching domains (with spaces)" { + run __check_completion 'defaults -currentHost read bash\ i' + assert_line -n 0 "Bash\ It" +} + +@test "completion defaults: -currentHost read BASH* - show matching domains (with spaces)" { + run __check_completion 'defaults -currentHost read BASH\ I' + assert_line -n 0 "Bash\ It" +} + +@test "completion defaults: -host some.computer.name read - show all domains" { + run __check_completion 'defaults -host some.computer.name read ' + assert_line -n 0 "NSGlobalDomain Bash\ It -app" +} + +@test "completion defaults: -host some.computer.name read nsg* - show matching domains" { + run __check_completion 'defaults -host some.computer.name read nsg' + assert_line -n 0 "NSGlobalDomain" +} + +@test "completion defaults: -host some.computer.name read NSG* - show matching domains" { + run __check_completion 'defaults -host some.computer.name read NSG' + assert_line -n 0 "NSGlobalDomain" +} + +@test "completion defaults: -host some.computer.name read bash* - show matching domains" { + run __check_completion 'defaults -host some.computer.name read bash' + assert_line -n 0 "Bash\ It" +} + +@test "completion defaults: -host some.computer.name read BASH* - show matching domains" { + run __check_completion 'defaults -host some.computer.name read BASH' + assert_line -n 0 "Bash\ It" +} + +@test "completion defaults: -host some.computer.name read bash* - show matching domains (with spaces)" { + run __check_completion 'defaults -host some.computer.name read bash\ i' + assert_line -n 0 "Bash\ It" +} + +@test "completion defaults: -host some.computer.name read BASH* - show matching domains (with spaces)" { + run __check_completion 'defaults -host some.computer.name read BASH\ I' + assert_line -n 0 "Bash\ It" +} diff --git a/vendor/github.com/rcaloras/bash-preexec/bash-preexec.sh b/vendor/github.com/rcaloras/bash-preexec/bash-preexec.sh index c23d0381..5f1208c3 100644 --- a/vendor/github.com/rcaloras/bash-preexec/bash-preexec.sh +++ b/vendor/github.com/rcaloras/bash-preexec/bash-preexec.sh @@ -32,11 +32,20 @@ # using: the "DEBUG" trap, and the "PROMPT_COMMAND" variable. If you override # either of these after bash-preexec has been installed it will most likely break. +# Make sure this is bash that's running and return otherwise. +if [[ -z "${BASH_VERSION:-}" ]]; then + return 1; +fi + # Avoid duplicate inclusion -if [[ "${__bp_imported:-}" == "defined" ]]; then +if [[ -n "${bash_preexec_imported:-}" ]]; then return 0 fi -__bp_imported="defined" +bash_preexec_imported="defined" + +# WARNING: This variable is no longer used and should not be relied upon. +# Use ${bash_preexec_imported} instead. +__bp_imported="${bash_preexec_imported}" # Should be available to each precmd and preexec # functions, should they want it. $? and $_ are available as $? and $_, but @@ -70,7 +79,8 @@ __bp_require_not_readonly() { # history even if it starts with a space. __bp_adjust_histcontrol() { local histcontrol - histcontrol="${HISTCONTROL//ignorespace}" + histcontrol="${HISTCONTROL:-}" + histcontrol="${histcontrol//ignorespace}" # Replace ignoreboth with ignoredups if [[ "$histcontrol" == *"ignoreboth"* ]]; then histcontrol="ignoredups:${histcontrol//ignoreboth}" @@ -85,6 +95,10 @@ __bp_adjust_histcontrol() { # and unset as soon as the trace hook is run. __bp_preexec_interactive_mode="" +# These arrays are used to add functions to be run before, or after, prompts. +declare -a precmd_functions +declare -a preexec_functions + # Trims leading and trailing whitespace from $2 and writes it to the variable # name passed as $1 __bp_trim_whitespace() { @@ -154,7 +168,7 @@ __bp_set_ret_value() { __bp_in_prompt_command() { local prompt_command_array - IFS=$'\n;' read -rd '' -a prompt_command_array <<< "$PROMPT_COMMAND" + IFS=$'\n;' read -rd '' -a prompt_command_array <<< "${PROMPT_COMMAND:-}" local trimmed_arg __bp_trim_whitespace trimmed_arg "${1:-}" @@ -292,7 +306,8 @@ __bp_install() { local existing_prompt_command # Remove setting our trap install string and sanitize the existing prompt command string - existing_prompt_command="${PROMPT_COMMAND//$__bp_install_string[;$'\n']}" # Edge case of appending to PROMPT_COMMAND + existing_prompt_command="${PROMPT_COMMAND:-}" + existing_prompt_command="${existing_prompt_command//$__bp_install_string[;$'\n']}" # Edge case of appending to PROMPT_COMMAND existing_prompt_command="${existing_prompt_command//$__bp_install_string}" __bp_sanitize_string existing_prompt_command "$existing_prompt_command" @@ -318,17 +333,12 @@ __bp_install() { # after our session has started. This allows bash-preexec to be included # at any point in our bash profile. __bp_install_after_session_init() { - # Make sure this is bash that's running this and return otherwise. - if [[ -z "${BASH_VERSION:-}" ]]; then - return 1; - fi - # bash-preexec needs to modify these variables in order to work correctly # if it can't, just stop the installation __bp_require_not_readonly PROMPT_COMMAND HISTCONTROL HISTTIMEFORMAT || return local sanitized_prompt_command - __bp_sanitize_string sanitized_prompt_command "$PROMPT_COMMAND" + __bp_sanitize_string sanitized_prompt_command "${PROMPT_COMMAND:-}" if [[ -n "$sanitized_prompt_command" ]]; then PROMPT_COMMAND=${sanitized_prompt_command}$'\n' fi; diff --git a/vendor/init.d/preexec.bash b/vendor/init.d/preexec.bash deleted file mode 100644 index 296b478a..00000000 --- a/vendor/init.d/preexec.bash +++ /dev/null @@ -1,3 +0,0 @@ -# shellcheck shell=bash -# shellcheck disable=1090 -source "${BASH_IT}"/vendor/github.com/rcaloras/bash-preexec/bash-preexec.sh