diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0eee145c..8e772ec5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,7 +7,7 @@ jobs: bats-test: strategy: matrix: - os: [ubuntu-20.04, ubuntu-18.04, macos-10.15, macos-11] + os: [ubuntu-20.04, ubuntu-22.04, macos-12, macos-11] runs-on: ${{ matrix.os }} @@ -49,10 +49,17 @@ jobs: uses: actions/setup-python@v2 with: python-version: 3.8 + # - name: Update APT Package Lists + # run: sudo apt-get update - name: Install shfmt run: GO111MODULE=on go get mvdan.cc/sh/v3/cmd/shfmt - name: Install shellcheck - run: brew install shellcheck + env: + scversion: stable # Or latest, vxx, etc + run: | + wget -qO- "https://github.com/koalaman/shellcheck/releases/download/${scversion?}/shellcheck-${scversion?}.linux.x86_64.tar.xz" | tar -xJv "shellcheck-${scversion}/shellcheck" + sudo cp "shellcheck-${scversion}/shellcheck" /usr/bin/ + shellcheck --version - name: Install pre-commit run: python3 -m pip install -r test/lint-requirements.txt - name: Run lint diff --git a/aliases/available/general.aliases.bash b/aliases/available/general.aliases.bash index 42930ab4..2511aab8 100644 --- a/aliases/available/general.aliases.bash +++ b/aliases/available/general.aliases.bash @@ -34,7 +34,7 @@ fi alias c='clear' alias cls='clear' -alias edit='${EDITOR:-${ALTERNATE_EDITOR?}}' +alias edit='${EDITOR:-${ALTERNATE_EDITOR:-nano}}' alias pager='${PAGER:=less}' alias q='exit' @@ -71,10 +71,6 @@ alias rd='rmdir' # Shorten extract alias xt='extract' -# sudo editors -alias svim='sudo ${VISUAL:-vim}' -alias snano='sudo nano' - # Display whatever file is regular file or folder function catt() { for i in "$@"; do diff --git a/aliases/available/git.aliases.bash b/aliases/available/git.aliases.bash index 507037e1..5572c932 100644 --- a/aliases/available/git.aliases.bash +++ b/aliases/available/git.aliases.bash @@ -8,13 +8,18 @@ alias get='git' alias ga='git add' alias gall='git add -A' alias gap='git add -p' +alias gav='git add -v' # branch alias gb='git branch' -alias gbD='git branch -D' -alias gba='git branch -a' +alias gba='git branch --all' alias gbd='git branch -d' -alias gbm='git branch -m' +alias gbD='git branch -D' +alias gbl='git branch --list' +alias gbla='git branch --list --all' +alias gblr='git branch --list --remotes' +alias gbm='git branch --move' +alias gbr='git branch --remotes' alias gbt='git branch --track' alias gdel='git branch -D' diff --git a/aliases/available/kubectl.aliases.bash b/aliases/available/kubectl.aliases.bash index aaca4ca2..ce01bdaf 100644 --- a/aliases/available/kubectl.aliases.bash +++ b/aliases/available/kubectl.aliases.bash @@ -1,20 +1,16 @@ # 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 -} - -_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 diff --git a/aliases/available/terraform.aliases.bash b/aliases/available/terraform.aliases.bash index baa9b0c7..fedd3198 100644 --- a/aliases/available/terraform.aliases.bash +++ b/aliases/available/terraform.aliases.bash @@ -2,6 +2,7 @@ about-alias 'Aliases for Terraform and Terragrunt' alias tf='terraform' +alias tfi='tf init' alias tfv='terraform validate' alias tfp='terraform plan' alias tfa='terraform apply' diff --git a/clean_files.txt b/clean_files.txt index ce72829f..491a1292 100644 --- a/clean_files.txt +++ b/clean_files.txt @@ -33,6 +33,7 @@ lint_clean_files.sh # 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 @@ -46,6 +47,7 @@ completion/available/docker-machine.completion.bash completion/available/docker.completion.bash completion/available/dotnet.completion.bash completion/available/export.completion.bash +completion/available/flutter.completion.bash completion/available/gcloud.completion.bash completion/available/gem.completion.bash completion/available/git.completion.bash @@ -75,6 +77,7 @@ completion/available/system.completion.bash completion/available/vault.completion.bash completion/available/vuejs.completion.bash completion/available/wpscan.completion.bash +completion/available/yarn.completion.bash # libraries lib/appearance.bash diff --git a/completion/available/aliases.completion.bash b/completion/available/aliases.completion.bash index f9cc1ed1..3e45e960 100644 --- a/completion/available/aliases.completion.bash +++ b/completion/available/aliases.completion.bash @@ -50,7 +50,7 @@ function _bash-it-component-completion-callback-on-init-aliases() { fi # skip aliases to pipes, boolean control structures and other command lists - chars='\|\&\;\)\(\n\<\>' + chars=$'|&;()<>\n' if [[ "${alias_defn}" =~ [$chars] ]]; then continue fi diff --git a/completion/available/awscli.completion.bash b/completion/available/awscli.completion.bash index a3041837..6b2c90ff 100644 --- a/completion/available/awscli.completion.bash +++ b/completion/available/awscli.completion.bash @@ -1,6 +1,5 @@ # shellcheck shell=bash -if _command_exists aws_completer -then +if _command_exists aws_completer; then complete -C "$(command -v aws_completer)" aws fi diff --git a/completion/available/fabric.completion.bash b/completion/available/fabric.completion.bash index 6f746454..a8984d9c 100644 --- a/completion/available/fabric.completion.bash +++ b/completion/available/fabric.completion.bash @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +# shellcheck shell=bash # # Bash completion support for Fabric (http://fabfile.org/) # @@ -91,7 +91,7 @@ function __fab_completion() { -*) if [[ -z "${__FAB_COMPLETION_LONG_OPT}" ]]; then export __FAB_COMPLETION_LONG_OPT=$( - fab --help | egrep -o "\-\-[A-Za-z_\-]+\=?" | sort -u) + fab --help | grep -E -o "\-\-[A-Za-z_\-]+\=?" | sort -u) fi opts="${__FAB_COMPLETION_LONG_OPT}" ;; @@ -101,7 +101,7 @@ function __fab_completion() { # -*) # if [[ -z "${__FAB_COMPLETION_SHORT_OPT}" ]]; then # export __FAB_COMPLETION_SHORT_OPT=$( - # fab --help | egrep -o "^ +\-[A-Za-z_\]" | sort -u) + # fab --help | grep -E -o "^ +\-[A-Za-z_\]" | sort -u) # fi # opts="${__FAB_COMPLETION_SHORT_OPT}" # ;; diff --git a/completion/available/flutter.completion.bash b/completion/available/flutter.completion.bash index 62befc82..7dde5a07 100644 --- a/completion/available/flutter.completion.bash +++ b/completion/available/flutter.completion.bash @@ -1,5 +1,5 @@ -#!/usr/bin/bash +# shellcheck shell=bash if _command_exists flutter; then - eval "$(flutter bash-completion)" + eval "$(flutter bash-completion)" fi diff --git a/completion/available/gradle.completion.bash b/completion/available/gradle.completion.bash index 35971d50..ef9677c6 100644 --- a/completion/available/gradle.completion.bash +++ b/completion/available/gradle.completion.bash @@ -1,3 +1,5 @@ +# shellcheck shell=bash + # Copyright (c) 2017 Eric Wendelin # Permission is hereby granted, free of charge, to any person obtaining a copy of @@ -66,7 +68,7 @@ __gradle-generate-script-cache() { if [[ ! $(find $cache_dir/$cache_name -mmin -$cache_ttl_mins 2>/dev/null) ]]; then # Cache all Gradle scripts - local gradle_build_scripts=$(find $project_root_dir -type f -name "*.gradle" -o -name "*.gradle.kts" 2>/dev/null | egrep -v "$script_exclude_pattern") + local gradle_build_scripts=$(find $project_root_dir -type f -name "*.gradle" -o -name "*.gradle.kts" 2>/dev/null | grep -E -v "$script_exclude_pattern") printf "%s\n" "${gradle_build_scripts[@]}" > $cache_dir/$cache_name fi } diff --git a/completion/available/makefile.completion.bash b/completion/available/makefile.completion.bash index e72ba6fd..018586ca 100644 --- a/completion/available/makefile.completion.bash +++ b/completion/available/makefile.completion.bash @@ -1,3 +1,5 @@ +# shellcheck shell=bash + # Bash completion for Makefile # Loosely adapted from http://stackoverflow.com/a/38415982/1472048 @@ -17,7 +19,7 @@ _makecomplete() { for f in "${files[@]}" ; do while IFS='' read -r line ; do targets+=("$line") - done < <(grep -oE '^[a-zA-Z0-9_-]+:([^=]|$)' "$f" | cut -d':' -f1) + done < <(grep -E -o '^[a-zA-Z0-9_-]+:([^=]|$)' "$f" | cut -d':' -f1) done [ "${#targets[@]}" -eq 0 ] && return 0 diff --git a/completion/available/projects.completion.bash b/completion/available/projects.completion.bash index 90735ee1..df4f5754 100644 --- a/completion/available/projects.completion.bash +++ b/completion/available/projects.completion.bash @@ -7,7 +7,7 @@ _is_function _rl_enabled || _pj() { _is_function _init_completion || return _is_function _rl_enabled || return - [ -n "$PROJECT_PATHS" ] || return + [ -n "$BASH_IT_PROJECT_PATHS" ] || return shift [ "$1" == "open" ] && shift @@ -21,7 +21,7 @@ _pj() { local -r mark_dirs=$(_rl_enabled mark-directories && echo y) local -r mark_symdirs=$(_rl_enabled mark-symlinked-directories && echo y) - for i in ${PROJECT_PATHS//:/$'\n'}; do + for i in ${BASH_IT_PROJECT_PATHS//:/$'\n'}; do # create an array of matched subdirs k="${#COMPREPLY[@]}" for j in $( compgen -d $i/$cur ); do diff --git a/completion/available/yarn.completion.bash b/completion/available/yarn.completion.bash new file mode 100644 index 00000000..de124085 --- /dev/null +++ b/completion/available/yarn.completion.bash @@ -0,0 +1,5 @@ +# shellcheck shell=bash +about-completion "yarn cli completions" + +# shellcheck disable=SC1090 source=../../vendor/github.com/dsifford/yarn-completion/yarn +source "${BASH_IT}/vendor/github.com/dsifford/yarn-completion/yarn" diff --git a/docs/README.md b/docs/README.md index f3d31a14..b307a7ab 100644 --- a/docs/README.md +++ b/docs/README.md @@ -4,7 +4,6 @@ ![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) -[![Join the chat at https://web.libera.chat/?channel=#bash-it](https://img.shields.io/badge/chat-on%20Libera.Chat-brightgreen.svg)](https://web.libera.chat/?channel=#bash-it) **Bash-it** is a collection of community Bash commands and scripts for Bash 3.2+. (And a shameless ripoff of [oh-my-zsh](https://github.com/robbyrussell/oh-my-zsh) :smiley:) diff --git a/docs/conf.py b/docs/conf.py index cacc2d50..f96485c6 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -14,7 +14,6 @@ # import sys # sys.path.insert(0, os.path.abspath('.')) - # -- Project information ----------------------------------------------------- project = 'Bash-it' @@ -24,7 +23,6 @@ author = 'Bash-it Team' # The full version, including alpha/beta/rc tags release = '' - # -- General configuration --------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be @@ -41,8 +39,7 @@ templates_path = ['_templates'] # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This pattern also affects html_static_path and html_extra_path. -exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] - +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', "venv"] # -- Options for HTML output ------------------------------------------------- diff --git a/docs/requirements.txt b/docs/requirements.txt index b3015c81..7b7fcdd1 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,4 +1,4 @@ -sphinx==3.2.1 +sphinx==4.5.0 sphinx-rtd-theme==0.5.0 sphinxemoji==0.1.8 docutils==0.17.1 diff --git a/docs/themes-list/barbuk.rst b/docs/themes-list/barbuk.rst index 15fb0ef5..79477524 100644 --- a/docs/themes-list/barbuk.rst +++ b/docs/themes-list/barbuk.rst @@ -8,13 +8,35 @@ A minimal theme with a clean git prompt Provided Information -------------------- - * Current git remote tool logo (support: github, gitlab, bitbucket) * Current path (red when user is root) * Current git info * Last command exit code (only shown when the exit code is greater than 0) * user@hostname for ssh connection +Default configuration +^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: bash + + BARBUK_PROMPT="git-uptream-remote-logo ssh path scm python_venv ruby node terraform cloud duration exit" + +You can override BARBUK_PROMPT to display only the desired information. + +available block: + +* git-uptream-remote-logo +* ssh +* path +* scm +* python_venv +* ruby +* node +* terraform +* cloud +* duration +* exit + Fonts and glyphs ---------------- @@ -39,6 +61,12 @@ Default theme glyphs BARBUK_EXIT_CODE_ICON=' ' BARBUK_PYTHON_VENV_CHAR=' ' BARBUK_COMMAND_DURATION_ICON='  ' + BARBUK_RUBY_CHAR=' ' + BARBUK_NODE_CHAR=' ' + BARBUK_TERRAFORM_CHAR="❲t❳ " + BARBUK_AWS_PROFILE_CHAR=" aws " + BARBUK_SCALEWAY_PROFILE_CHAR=" scw " + BARBUK_GCLOUD_CHAR=" gcp " Customize glyphs ^^^^^^^^^^^^^^^^ diff --git a/docs/troubleshooting.rst b/docs/troubleshooting.rst index 6503699a..93d9113a 100644 --- a/docs/troubleshooting.rst +++ b/docs/troubleshooting.rst @@ -8,9 +8,38 @@ Table of Contents * `I'm stuck in the LightDM login screen after setting up bash-it. `_ +* `I'm getting strange line break and wrapping behaviour on macOS. `_ + I'm stuck in the LightDM login screen after setting up bash-it ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ **Possible issue**\ : `#672 `_ **Solution**\ : Check `this comment `_ for detailed information about the cause and solution for this issue. + +I'm getting strange line break and wrapping behaviour on macOS +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +**Possible issue**\ : `#1614 `_ + +**Solution**\ : Bash-it requires Bash 4.?? or later to run correctly. Any reasonably current Linux distribution should have shipped with a compatible version of Bash. However, macOS users must upgrade from the included, obsolete Bash version 3. While some functionality might work with Bash 3, there is no guarantee that everything will work perfectly. Thus, we recommend using `Homebrew `_ to ensure Bash is up to date: + +x86 Mac +^^^^^^^ + + .. code-block:: bash + + brew install bash + sudo sh -c 'echo /usr/local/bin/bash >> /etc/shells' + chsh -s /usr/local/bin/bash + +M1 Mac +^^^^^^ + +Homebrew's default installation location on M1 is ``/opt/homebrew/bin/``: + + .. code-block:: bash + + brew install bash + sudo sh -c 'echo /opt/homebrew/bin/bash >> /etc/shells' + chsh -s /opt/homebrew/bin/bash diff --git a/install.sh b/install.sh index 2bb78a3f..58c25537 100755 --- a/install.sh +++ b/install.sh @@ -12,7 +12,6 @@ function _bash-it_show_usage() { echo "--no-modify-config (-n): Do not modify existing config file" echo "--append-to-config (-a): Keep existing config file and append bash-it templates at the end" echo "--overwrite-backup (-f): Overwrite existing backup" - exit 0 } # enable a thing diff --git a/lib/command_duration.bash b/lib/command_duration.bash index 686267fb..2b5e1b4b 100644 --- a/lib/command_duration.bash +++ b/lib/command_duration.bash @@ -2,12 +2,24 @@ # # Functions for measuring and reporting how long a command takes to run. -: "${COMMAND_DURATION_START_SECONDS:=${EPOCHREALTIME:-$SECONDS}}" +# Get shell duration in decimal format regardless of runtime locale. +# Notice: This function runs as a sub-shell - notice '(' vs '{'. +function _shell_duration_en() ( + # DFARREL You would think LC_NUMERIC would do it, but not working in my local + LC_ALL='en_US.UTF-8' + printf "%s" "${EPOCHREALTIME:-$SECONDS}" +) + +: "${COMMAND_DURATION_START_SECONDS:=$(_shell_duration_en)}" : "${COMMAND_DURATION_ICON:=🕘}" : "${COMMAND_DURATION_MIN_SECONDS:=1}" function _command_duration_pre_exec() { - COMMAND_DURATION_START_SECONDS="${EPOCHREALTIME:-$SECONDS}" + COMMAND_DURATION_START_SECONDS="$(_shell_duration_en)" +} + +function _command_duration_pre_cmd() { + COMMAND_DURATION_START_SECONDS="" } function _dynamic_clock_icon { @@ -20,14 +32,18 @@ function _dynamic_clock_icon { function _command_duration() { [[ -n "${BASH_IT_COMMAND_DURATION:-}" ]] || return + [[ -n "${COMMAND_DURATION_START_SECONDS:-}" ]] || 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##*.})) - local current_time="${EPOCHREALTIME:-$SECONDS}" + command_start_deciseconds="${command_start_deciseconds:0:1}" + local current_time + current_time="$(_shell_duration_en)" 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 @@ -43,17 +59,18 @@ function _command_duration() { command_duration=0 fi - if ((command_duration > 0)); then + if ((command_duration >= COMMAND_DURATION_MIN_SECONDS)); 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" + _dynamic_clock_icon "${command_duration}" + if ((minutes > 0)); then + printf "%s %s%dm %ds" "${COMMAND_DURATION_ICON:-}" "${COMMAND_DURATION_COLOR:-}" "$minutes" "$seconds" + else + printf "%s %s%d.%01ds" "${COMMAND_DURATION_ICON:-}" "${COMMAND_DURATION_COLOR:-}" "$seconds" "$deciseconds" + fi fi } _bash_it_library_finalize_hook+=("safe_append_preexec '_command_duration_pre_exec'") +_bash_it_library_finalize_hook+=("safe_append_prompt_command '_command_duration_pre_cmd'") diff --git a/lib/helpers.bash b/lib/helpers.bash index c0ce2eb3..3675b0f2 100644 --- a/lib/helpers.bash +++ b/lib/helpers.bash @@ -211,7 +211,7 @@ function _is_function() { _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 + if LC_ALL=C type -t "$1" | _bash-it-fgrep -q 'function'; then return 0 else _log_debug "$msg" @@ -290,6 +290,7 @@ function _bash-it-update-() { 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" + popd > /dev/null || return return 1 fi @@ -334,7 +335,7 @@ function _bash-it-update-() { log_color="%Cred" fi - git log --format="${log_color}%h: %s (%an)" "${revision}" + git log --no-merges --format="${log_color}%h: %s (%an)" "${revision}" echo "" if [[ -n "${silent}" ]]; then diff --git a/lib/utilities.bash b/lib/utilities.bash index 8ea6b98c..75e914b8 100644 --- a/lib/utilities.bash +++ b/lib/utilities.bash @@ -60,15 +60,21 @@ function _bash-it-array-dedup() { printf '%s\n' "$@" | sort -u } -# Outputs a full path of the grep found on the filesystem +# Runs `grep` with *just* the provided arguments function _bash-it-grep() { - : "${BASH_IT_GREP:=$(type -p egrep || type -p grep)}" - printf "%s" "${BASH_IT_GREP:-/usr/bin/grep}" + : "${BASH_IT_GREP:=$(type -P grep)}" + "${BASH_IT_GREP:-/usr/bin/grep}" "$@" } -# Runs `grep` with extended regular expressions +# Runs `grep` with fixed-string expressions (-F) +function _bash-it-fgrep() { + : "${BASH_IT_GREP:=$(type -P grep)}" + "${BASH_IT_GREP:-/usr/bin/grep}" -F "$@" +} + +# Runs `grep` with extended regular expressions (-E) function _bash-it-egrep() { - : "${BASH_IT_GREP:=$(type -p egrep || type -p grep)}" + : "${BASH_IT_GREP:=$(type -P grep)}" "${BASH_IT_GREP:-/usr/bin/grep}" -E "$@" } @@ -150,12 +156,12 @@ function _bash-it-component-list-matching() { 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-help "${component}" | _bash-it-fgrep '[x]' | awk '{print $1}' | sort -u } 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 + _bash-it-component-help "${component}" | _bash-it-fgrep -v '[x]' | awk '{print $1}' | sort -u } # Checks if a given item is enabled for a particular component/file-type. diff --git a/lint_clean_files.sh b/lint_clean_files.sh index 26650b16..cc268604 100755 --- a/lint_clean_files.sh +++ b/lint_clean_files.sh @@ -8,8 +8,8 @@ # shellcheck disable=SC2002 # Prefer 'cat' for cleaner script mapfile -t FILES < <( cat clean_files.txt \ - | grep -v -E '^\s*$' \ - | grep -v -E '^\s*#' \ + | grep -E -v '^\s*$' \ + | grep -E -v '^\s*#' \ | xargs -n1 -I{} find "{}" -type f ) diff --git a/plugins/available/aws.plugin.bash b/plugins/available/aws.plugin.bash index 54a86691..14d26cae 100644 --- a/plugins/available/aws.plugin.bash +++ b/plugins/available/aws.plugin.bash @@ -1,3 +1,4 @@ +# shellcheck shell=bash cite about-plugin about-plugin 'AWS helper functions' @@ -40,13 +41,13 @@ function __awskeys_help { function __awskeys_get { local ln=$(grep -n "\[ *$1 *\]" "${AWS_SHARED_CREDENTIALS_FILE}" | cut -d ":" -f 1) if [[ -n "${ln}" ]]; then - tail -n +${ln} "${AWS_SHARED_CREDENTIALS_FILE}" | egrep -m 2 "aws_access_key_id|aws_secret_access_key" - tail -n +${ln} "${AWS_SHARED_CREDENTIALS_FILE}" | egrep -m 1 "aws_session_token" + tail -n +${ln} "${AWS_SHARED_CREDENTIALS_FILE}" | grep -F -m 2 -e "aws_access_key_id" -e "aws_secret_access_key" + tail -n +${ln} "${AWS_SHARED_CREDENTIALS_FILE}" | grep -F -m 1 "aws_session_token" fi } function __awskeys_list { - local credentials_list="$((egrep '^\[ *[a-zA-Z0-9_-]+ *\]$' "${AWS_SHARED_CREDENTIALS_FILE}"; grep "\[profile" "${AWS_CONFIG_FILE}" | sed "s|\[profile |\[|g") | sort | uniq)" + local credentials_list="$((grep -E '^\[ *[a-zA-Z0-9_-]+ *\]$' "${AWS_SHARED_CREDENTIALS_FILE}"; grep "\[profile" "${AWS_CONFIG_FILE}" | sed "s|\[profile |\[|g") | sort | uniq)" if [[ -n $"{credentials_list}" ]]; then echo -e "Available credentials profiles:\n" for profile in ${credentials_list}; do diff --git a/plugins/available/cmd-returned-notify.plugin.bash b/plugins/available/cmd-returned-notify.plugin.bash index 88c07722..e6d221fa 100644 --- a/plugins/available/cmd-returned-notify.plugin.bash +++ b/plugins/available/cmd-returned-notify.plugin.bash @@ -4,7 +4,8 @@ about-plugin 'Alert (BEL) when process ends after a threshold of seconds' function precmd_return_notification() { local command_start="${COMMAND_DURATION_START_SECONDS:=0}" - local current_time="${EPOCHREALTIME:-$SECONDS}" + local current_time + current_time="$(_shell_duration_en)" local -i command_duration="$((${current_time%.*} - ${command_start%.*}))" if [[ "${command_duration}" -gt "${NOTIFY_IF_COMMAND_RETURNS_AFTER:-5}" ]]; then printf '\a' diff --git a/plugins/available/colors.plugin.bash b/plugins/available/colors.plugin.bash index 47f55609..73c144b8 100644 --- a/plugins/available/colors.plugin.bash +++ b/plugins/available/colors.plugin.bash @@ -8,13 +8,13 @@ function __() { function __make_ansi() { next=$1 shift - echo "\[\e[$("__$next" "$@")m\]" + echo -e "\[\e[$("__$next" "$@")m\]" } function __make_echo() { next=$1 shift - echo "\033[$("__$next" "$@")m" + echo -e "\033[$("__$next" "$@")m" } function __reset() { diff --git a/plugins/available/dirs.plugin.bash b/plugins/available/dirs.plugin.bash index 34468fa0..55d2e88a 100644 --- a/plugins/available/dirs.plugin.bash +++ b/plugins/available/dirs.plugin.bash @@ -63,12 +63,15 @@ function dirs-help() { 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 - touch "${BASH_IT_DIRS_BKS?}" + mkdir -p "${BASH_IT_DIRS_BKS%/*}" + if [[ -f ~/.dirs ]]; then + mv -vn ~/.dirs "${BASH_IT_DIRS_BKS?}" + # shellcheck disable=SC1090 + source "${BASH_IT_DIRS_BKS?}" + else + touch "${BASH_IT_DIRS_BKS?}" + fi fi alias L='cat "${BASH_IT_DIRS_BKS?}"' diff --git a/plugins/available/jekyll.plugin.bash b/plugins/available/jekyll.plugin.bash index d818b076..3c12d826 100644 --- a/plugins/available/jekyll.plugin.bash +++ b/plugins/available/jekyll.plugin.bash @@ -30,8 +30,8 @@ function editpost() { pushd "${SITE}/_posts" > /dev/null || return 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}")" + DATE="$(echo "${POST}" | grep -E -o "[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}")" + TITLE="$(grep -E -o "title: (.+)" < "${POST}")" TITLE="${TITLE/title: /}" echo "${COUNTER}) ${DATE} ${TITLE}" POSTS[COUNTER]="$POST" diff --git a/plugins/available/postgres.plugin.bash b/plugins/available/postgres.plugin.bash index 8f239985..9f66152b 100644 --- a/plugins/available/postgres.plugin.bash +++ b/plugins/available/postgres.plugin.bash @@ -1,3 +1,4 @@ +# shellcheck shell=bash cite about-plugin about-plugin 'postgres helper functions' @@ -50,7 +51,7 @@ function postgres_status { function is_postgres_running { - $POSTGRES_BIN/pg_ctl -D $PGDATA status | egrep -o "no server running" + $POSTGRES_BIN/pg_ctl -D $PGDATA status | grep -F -o "no server running" } diff --git a/plugins/available/projects.plugin.bash b/plugins/available/projects.plugin.bash index 80386480..34fa001e 100644 --- a/plugins/available/projects.plugin.bash +++ b/plugins/available/projects.plugin.bash @@ -21,7 +21,7 @@ function pj() { # 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]}" ]]; then + if [[ ! -d "${dests[d]}/${proj}" ]]; then unset 'dests[d]' fi done diff --git a/test/plugins/cmd-returned-notify.plugin.bats b/test/plugins/cmd-returned-notify.plugin.bats index 04edad95..a5ae591a 100644 --- a/test/plugins/cmd-returned-notify.plugin.bats +++ b/test/plugins/cmd-returned-notify.plugin.bats @@ -9,7 +9,7 @@ function local_setup_file() { @test "plugins cmd-returned-notify: notify after elapsed time" { export NOTIFY_IF_COMMAND_RETURNS_AFTER=0 - export COMMAND_DURATION_START_SECONDS="${EPOCHREALTIME:-$SECONDS}" + export COMMAND_DURATION_START_SECONDS="$(_shell_duration_en)" sleep 1 run precmd_return_notification assert_success @@ -18,7 +18,7 @@ function local_setup_file() { @test "plugins cmd-returned-notify: do not notify before elapsed time" { export NOTIFY_IF_COMMAND_RETURNS_AFTER=10 - export COMMAND_DURATION_START_SECONDS="${EPOCHREALTIME:-$SECONDS}" + export COMMAND_DURATION_START_SECONDS="$(_shell_duration_en)" sleep 1 run precmd_return_notification assert_success @@ -34,7 +34,7 @@ function local_setup_file() { @test "lib command_duration: preexec set COMMAND_DURATION_START_SECONDS" { export COMMAND_DURATION_START_SECONDS= assert_equal "${COMMAND_DURATION_START_SECONDS}" "" - NOW="${EPOCHREALTIME:-$SECONDS}" + NOW="$(_shell_duration_en)" _command_duration_pre_exec # We need to make sure to account for nanoseconds... assert_equal "${COMMAND_DURATION_START_SECONDS%.*}" "${NOW%.*}" diff --git a/themes/agnoster/agnoster.theme.bash b/themes/agnoster/agnoster.theme.bash index 20c184f3..d5bac5ca 100644 --- a/themes/agnoster/agnoster.theme.bash +++ b/themes/agnoster/agnoster.theme.bash @@ -182,7 +182,7 @@ prompt_segment() { # declare -p codes if [[ $CURRENT_BG != NONE && $1 != "$CURRENT_BG" ]]; then - declare -a intermediate=("$(fg_color $CURRENT_BG)" "$(bg_color "$1")") + declare -a intermediate=("$(fg_color "$CURRENT_BG")" "$(bg_color "$1")") debug "pre prompt " "$(ansi intermediate[@])" PR="$PR $(ansi intermediate[@])$SEGMENT_SEPARATOR" debug "post prompt " "$(ansi codes[@])" diff --git a/themes/barbuk/barbuk.theme.bash b/themes/barbuk/barbuk.theme.bash index b614d148..6e6eb88a 100644 --- a/themes/barbuk/barbuk.theme.bash +++ b/themes/barbuk/barbuk.theme.bash @@ -1,8 +1,11 @@ # shellcheck shell=bash # shellcheck disable=SC2034 # Expected behavior for themes. -# shellcheck disable=SC2154 #TODO: fix these all. + +# Prompt defaut configuration +BARBUK_PROMPT=${BARBUK_PROMPT:="git-uptream-remote-logo ssh path scm python_venv ruby node terraform cloud duration exit"} # Theme custom glyphs +# SCM SCM_GIT_CHAR_GITLAB=${BARBUK_GITLAB_CHAR:=' '} SCM_GIT_CHAR_BITBUCKET=${BARBUK_BITBUCKET_CHAR:=' '} SCM_GIT_CHAR_GITHUB=${BARBUK_GITHUB_CHAR:=' '} @@ -10,13 +13,20 @@ SCM_GIT_CHAR_DEFAULT=${BARBUK_GIT_DEFAULT_CHAR:=' '} SCM_GIT_CHAR_ICON_BRANCH=${BARBUK_GIT_BRANCH_ICON:=''} SCM_HG_CHAR=${BARBUK_HG_CHAR:='☿ '} SCM_SVN_CHAR=${BARBUK_SVN_CHAR:='⑆ '} +# Exit code EXIT_CODE_ICON=${BARBUK_EXIT_CODE_ICON:=' '} +# Programming and tools PYTHON_VENV_CHAR=${BARBUK_PYTHON_VENV_CHAR:=' '} -COMMAND_DURATION_ICON=${BARBUK_COMMAND_DURATION_ICON:-"$bold_blue  "} +RUBY_CHAR=${BARBUK_RUBY_CHAR:=' '} +NODE_CHAR=${BARBUK_NODE_CHAR:=' '} +TERRAFORM_CHAR=${BARBUK_TERRAFORM_CHAR:="❲t❳ "} +# Cloud +AWS_PROFILE_CHAR=${BARBUK_AWS_PROFILE_CHAR:=" aws "} +SCALEWAY_PROFILE_CHAR=${BARBUK_SCALEWAY_PROFILE_CHAR:=" scw "} +GCLOUD_CHAR=${BARBUK_GCLOUD_CHAR:=" google "} # Command duration COMMAND_DURATION_MIN_SECONDS=${COMMAND_DURATION_MIN_SECONDS:-1} -COMMAND_DURATION_COLOR="$normal" # Ssh user and hostname display SSH_INFO=${BARBUK_SSH_INFO:=true} @@ -24,24 +34,32 @@ HOST_INFO=${BARBUK_HOST_INFO:=long} # Bash-it default glyphs customization SCM_NONE_CHAR= -SCM_THEME_PROMPT_DIRTY=" ${bold_red}✗" -SCM_THEME_PROMPT_CLEAN=" ${bold_green}✓" +SCM_THEME_PROMPT_DIRTY=" ${bold_red?}✗" +SCM_THEME_PROMPT_CLEAN=" ${bold_green?}✓" SCM_THEME_PROMPT_PREFIX="|" -SCM_THEME_PROMPT_SUFFIX="${green}| " -SCM_GIT_BEHIND_CHAR="${bold_red}↓${normal}" -SCM_GIT_AHEAD_CHAR="${bold_green}↑${normal}" +SCM_THEME_PROMPT_SUFFIX="${green?}| " +SCM_GIT_BEHIND_CHAR="${bold_red?}↓${normal?}" +SCM_GIT_AHEAD_CHAR="${bold_green?}↑${normal?}" SCM_GIT_UNTRACKED_CHAR="⌀" -SCM_GIT_UNSTAGED_CHAR="${bold_yellow}•${normal}" -SCM_GIT_STAGED_CHAR="${bold_green}+${normal}" -GIT_THEME_PROMPT_DIRTY=" ${bold_red}✗" -GIT_THEME_PROMPT_CLEAN=" ${bold_green}✓" -GIT_THEME_PROMPT_PREFIX="${cyan}" -GIT_THEME_PROMPT_SUFFIX="${cyan}" -SCM_THEME_BRANCH_TRACK_PREFIX="${normal} ⤏ ${cyan}" +SCM_GIT_UNSTAGED_CHAR="${bold_yellow?}•${normal?}" +SCM_GIT_STAGED_CHAR="${bold_green?}+${normal?}" +GIT_THEME_PROMPT_DIRTY=" ${bold_red?}✗" +GIT_THEME_PROMPT_CLEAN=" ${bold_green?}✓" +GIT_THEME_PROMPT_PREFIX="${cyan?}" +GIT_THEME_PROMPT_SUFFIX="${cyan?}" +SCM_THEME_BRANCH_TRACK_PREFIX="${normal?} ⤏ ${cyan?}" SCM_THEME_CURRENT_USER_PREFFIX='  ' SCM_GIT_SHOW_CURRENT_USER=false +NVM_THEME_PROMPT_PREFIX='' +NVM_THEME_PROMPT_SUFFIX='' +RVM_THEME_PROMPT_PREFIX='' +RVM_THEME_PROMPT_SUFFIX='' +RBENV_THEME_PROMPT_PREFIX=' ' +RBENV_THEME_PROMPT_SUFFIX='' +RBFU_THEME_PROMPT_PREFIX='' +RBFU_THEME_PROMPT_SUFFIX='' -function _git-uptream-remote-logo { +function __git-uptream-remote-logo_prompt() { [[ "$(_git-upstream)" == "" ]] && SCM_GIT_CHAR="$SCM_GIT_CHAR_DEFAULT" local remote remote_domain @@ -57,36 +75,72 @@ function _git-uptream-remote-logo { bitbucket) SCM_GIT_CHAR="$SCM_GIT_CHAR_BITBUCKET" ;; *) SCM_GIT_CHAR="$SCM_GIT_CHAR_DEFAULT" ;; esac + + echo "${purple?}$(scm_char)" } -function git_prompt_info { +function git_prompt_info() { git_prompt_vars - echo -e " on $SCM_GIT_CHAR_ICON_BRANCH $SCM_PREFIX$SCM_BRANCH$SCM_STATE$SCM_GIT_AHEAD$SCM_GIT_BEHIND$SCM_GIT_STASH$SCM_SUFFIX" + echo -e "on $SCM_GIT_CHAR_ICON_BRANCH $SCM_PREFIX$SCM_BRANCH$SCM_STATE$SCM_GIT_AHEAD$SCM_GIT_BEHIND$SCM_GIT_STASH$SCM_SUFFIX " } -function _exit-code { - if [[ "$1" -ne 0 ]]; then - exit_code=" ${purple}${EXIT_CODE_ICON}${yellow}${exit_code}${bold_orange}" +function __exit_prompt() { + if [[ "$exit_code" -ne 0 ]]; then + echo "${purple?}${EXIT_CODE_ICON}${yellow?}${exit_code}${bold_orange?} " else - exit_code="${bold_green}" + echo "${bold_green}" fi } -function _prompt { - local exit_code="$?" wrap_char=' ' dir_color=$green ssh_info='' python_venv='' host command_duration= - - command_duration=$(_command_duration) - - _exit-code exit_code - _git-uptream-remote-logo - - history -a - - # Detect root shell - if [ "$(whoami)" = root ]; then - dir_color=$red +function __aws_profile_prompt() { + if [[ -n "${AWS_PROFILE}" ]]; then + echo -n "${bold_purple?}${AWS_PROFILE_CHAR}${normal?}${AWS_PROFILE} " fi +} +function __scaleway_profile_prompt() { + if [[ -n "${SCW_PROFILE}" ]]; then + echo -n "${bold_purple?}${SCALEWAY_PROFILE_CHAR}${normal?}${SCW_PROFILE} " + fi +} + +function __gcloud_prompt() { + local active_gcloud_account="" + + active_gcloud_account="$(active_gcloud_account_prompt)" + [[ -n "${active_gcloud_account}" ]] && echo "${bold_purple?}${GCLOUD_CHAR}${normal?}${active_gcloud_account} " +} + +function __cloud_prompt() { + __aws_profile_prompt + __scaleway_profile_prompt + __gcloud_prompt +} + +function __terraform_prompt() { + local terraform_workspace="" + + if [ -d .terraform ]; then + terraform_workspace="$(terraform_workspace_prompt)" + [[ -n "${terraform_workspace}" ]] && echo "${bold_purple?}${TERRAFORM_CHAR}${normal?}${terraform_workspace} " + fi +} + +function __node_prompt() { + local node_version="" + + node_version="$(node_version_prompt)" + [[ -n "${node_version}" ]] && echo "${bold_purple?}${NODE_CHAR}${normal?}${node_version} " +} + +function __ruby_prompt() { + local ruby_version="" + + ruby_version="$(ruby_version_prompt)" + [[ -n "${ruby_version}" ]] && echo "${bold_purple?}${RUBY_CHAR}${normal?}${ruby_version} " +} + +function __ssh_prompt() { # Detect ssh if [[ -n "${SSH_CONNECTION}" ]] && [ "$SSH_INFO" = true ]; then if [ "$HOST_INFO" = long ]; then @@ -94,19 +148,56 @@ function _prompt { else host="\h" fi - ssh_info="${bold_blue}\u${bold_orange}@${cyan}$host ${bold_orange}in" + echo "${bold_blue?}\u${bold_orange?}@${cyan?}$host ${bold_orange?}in " fi +} +function __python_venv_prompt() { # Detect python venv if [[ -n "${CONDA_DEFAULT_ENV}" ]]; then - python_venv="$PYTHON_VENV_CHAR${CONDA_DEFAULT_ENV} " + echo "${bold_purple?}$PYTHON_VENV_CHAR${normal?}${CONDA_DEFAULT_ENV} " elif [[ -n "${VIRTUAL_ENV}" ]]; then - python_venv="$PYTHON_VENV_CHAR$(basename "${VIRTUAL_ENV}") " + echo "${bold_purple?}$PYTHON_VENV_CHAR${normal?}$(basename "${VIRTUAL_ENV}") " + fi +} + +function __path_prompt() { + local dir_color=${green?} + # Detect root shell + if [ "$(whoami)" = root ]; then + dir_color=${red?} + fi + + echo "${dir_color}\w${normal} " +} + +function __scm_prompt() { + scm_prompt_info +} + +function __duration_prompt() { + [[ -n "$command_duration" ]] && echo "${command_duration} " +} + +function __prompt-command() { + exit_code="$?" + command_duration=$(_command_duration) + local wrap_char + + # Generate prompt + PS1="\n " + for segment in $BARBUK_PROMPT; do + local info + info="$(__"${segment}"_prompt)" + [[ -n "${info}" ]] && PS1+="${info}" + done + + # Cut prompt when it's too long + if [[ ${#PS1} -gt $((COLUMNS * 2)) ]]; then + wrap_char="\n" fi - PS1="\\n${ssh_info} ${purple}$(scm_char)${python_venv}${dir_color}\\w${normal}$(scm_prompt_info)${command_duration}${exit_code}" - [[ ${#PS1} -gt $((COLUMNS * 2)) ]] && wrap_char="\\n" PS1="${PS1}${wrap_char}❯${normal} " } -safe_append_prompt_command _prompt +safe_append_prompt_command __prompt-command diff --git a/themes/parrot/parrot.theme.bash b/themes/parrot/parrot.theme.bash index a0d259dd..251eb942 100644 --- a/themes/parrot/parrot.theme.bash +++ b/themes/parrot/parrot.theme.bash @@ -2,7 +2,7 @@ # git branch parser function parse_git_branch() { - echo -e "\033[1;34m$(git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/(\1)/')\033[0m" + echo -e "\[\033[1;34m\]$(git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/(\1)/')\[\033[0m\]" } function parse_git_branch_no_color() { diff --git a/themes/rjorgenson/rjorgenson.theme.bash b/themes/rjorgenson/rjorgenson.theme.bash index 71d29e78..6e73c4a2 100644 --- a/themes/rjorgenson/rjorgenson.theme.bash +++ b/themes/rjorgenson/rjorgenson.theme.bash @@ -1,3 +1,5 @@ +# shellcheck shell=bash + # port of zork theme # set colors for use throughout the prompt @@ -50,7 +52,7 @@ function is_integer() { # helper function for todo-txt-count todo_txt_count() { if `hash todo.sh 2>&-`; then # is todo.sh installed - count=`todo.sh ls | egrep "TODO: [0-9]+ of ([0-9]+) tasks shown" | awk '{ print $4 }'` + count=`todo.sh ls | grep -E "TODO: [0-9]+ of ([0-9]+) tasks shown" | awk '{ print $4 }'` if is_integer $count; then # did we get a sane answer back echo "${BRACKET_COLOR}[${STRING_COLOR}T:$count${BRACKET_COLOR}]$normal" fi diff --git a/vendor/github.com/dsifford/yarn-completion/yarn b/vendor/github.com/dsifford/yarn-completion/yarn new file mode 100644 index 00000000..ff78e471 --- /dev/null +++ b/vendor/github.com/dsifford/yarn-completion/yarn @@ -0,0 +1,1208 @@ +# shellcheck shell=bash disable=2207 +# vim: set fdm=syntax fdl=0: +# +# Version: 0.17.0 +# Yarn Version: 1.22.11 +# +# bash completion for Yarn (https://github.com/yarnpkg/yarn) +# +# To enable on-demand completion loading, copy this file to one of the following locations: +# - $BASH_COMPLETION_USER_DIR/completions/yarn +# or +# - $XDG_DATA_HOME/bash-completion/completions/yarn +# or +# - ~/.local/share/bash-completion/completions/yarn +# + +### +# Parses and extracts data from package.json files. +# +# Usage: +# __yarn_get_package_fields [-g] [-t FIELDTYPE] +# +# Options: +# -g Parse global package.json file, if available +# -t FIELDTYPE The field type being parsed (array|boolean|number|object|string) [default: object] +# +# Notes: +# If FIELDTYPE is object, then the object keys are returned. +# If FIELDTYPE is array, boolean, number, or string, then the field values are returned. +# must be a first-level field in the json file. +## +__yarn_get_package_fields() { + declare cwd=$PWD field_type=object field_key opt package_dot_json OPTIND OPTARG + + while [[ -n $cwd ]]; do + if [[ -f "$cwd/package.json" ]]; then + package_dot_json="$cwd/package.json" + break + fi + cwd="${cwd%/*}" + done + + while getopts ":gt:" opt; do + case $opt in + g) + if [[ -f $HOME/.config/yarn/global/package.json ]]; then + package_dot_json="$HOME/.config/yarn/global/package.json" + elif [[ -f $HOME/.local/share/yarn/global/package.json ]]; then + package_dot_json="$HOME/.local/share/yarn/global/package.json" + elif [[ -f $HOME/.yarn/global/package.json ]]; then + package_dot_json="$HOME/.yarn/global/package.json" + else + package_dot_json="" + fi + ;; + t) + case "$OPTARG" in + array | boolean | number | object | string) + field_type="$OPTARG" + ;; + esac + ;; + *) ;; + + esac + done + shift $((OPTIND - 1)) + + field_key='"'$1'"' + + [[ ! -f $package_dot_json || ! $field_key ]] && return + + case "$field_type" in + object) + sed -n '/'"$field_key"':[[:space:]]*{/,/^[[:space:]]*}/{ + # exclude start and end patterns + //!{ + # extract the text between the first pair of double quotes + s/^[[:space:]]*"\([^"]*\).*/\1/p + } + }' "$package_dot_json" + ;; + array) + sed -n '/'"$field_key"':[[:space:]]*\[/,/^[[:space:]]*]/{ + # exclude start and end patterns + //!{ + # extract the text between the first pair of double quotes + s/^[[:space:]]*"\([^"]*\).*/\1/p + } + }' "$package_dot_json" + ;; + boolean | number) + sed -n 's/[[:space:]]*'"$field_key"':[[:space:]]*\([a-z0-9]*\)/\1/p' "$package_dot_json" + ;; + string) + sed -n 's/[[:space:]]*'"$field_key"':[[:space:]]*"\(.*\)".*/\1/p' "$package_dot_json" + ;; + esac +} + +### +# Count all command arguments starting at a given depth, excluding flags and +# flag arguments. +# +# Usage: +# __yarn_count_args [-d INT] +# +# Options: +# -d INT The start depth to begin counting [default: 0] +# +# Globals: +# *args +# cword +## +__yarn_count_args() { + args=0 + declare -i counter=0 depth=0 + declare arg_flag_pattern opt OPTIND + arg_flag_pattern="@($(tr ' ' '|' <<< "${arg_flags[*]}"))" + + while getopts ":d:" opt; do + case $opt in + d) + depth=$OPTARG + ;; + *) ;; + esac + done + shift $((OPTIND - 1)) + + while ((counter < cword)); do + case ${words[counter]} in + -* | =) ;; + *) + # shellcheck disable=SC2053 + if [[ ${words[counter - 1]} != $arg_flag_pattern ]]; then + if ((depth-- <= 0)); then + ((args++)) + fi + fi + ;; + esac + ((counter++)) + done +} + +### +# Retrieves the command or subcommand at a given depth, or the last occurring +# command or subcommand before the cursor location if no depth is given, or if +# depth exceeds cursor location. +# +# Usage: +# __yarn_get_command [-d INT] +# +# Options: +# -d INT Depth of command to retrieve. +# +# Globals: +# *cmd +# commands +# cword +# subcommands +# words +## +__yarn_get_command() { + declare -i counter=0 cmd_depth=0 OPTIND + declare cmdlist word opt + + while getopts ":d:" opt; do + case $opt in + d) + cmd_depth="$OPTARG" + ;; + *) ;; + esac + done + shift $((OPTIND - 1)) + + cmdlist="@($(tr ' ' '|' <<< "${commands[*]} ${subcommands[*]}"))" + cmd=yarn + + while ((counter < cword)); do + word="${words[counter]}" + case "$word" in + $cmdlist) + cmd="$word" + ((--cmd_depth == 0)) && break + ;; + esac + ((counter++)) + done +} + +### +# Global fallback completion generator if all else fails. +# +# Usage: +# __yarn_fallback +# +# Globals: +# cur +## +__yarn_fallback() { + case "$cur" in + -*) + COMPREPLY=($(compgen -W "$(__yarn_flags)" -- "$cur")) + ;; + *) + COMPREPLY=($(compgen -o plusdirs -f -- "$cur")) + ;; + esac +} + +### +# Process and merge local and global flags after removing the flags that +# have already been used. +# +# Usage: +# __yarn_flags +# +# Globals: +# flags +# global_flags +# words +## +__yarn_flags() { + declare word + declare -a existing_flags=() + + for word in "${words[@]}"; do + case "$word" in + -*) + existing_flags+=("$word") + ;; + esac + done + + LC_ALL=C comm -23 \ + <(echo "${flags[@]}" "${global_flags[@]}" | tr ' ' '\n' | LC_ALL=C sort -u) \ + <(echo "${existing_flags[@]}" | tr ' ' '\n' | LC_ALL=C sort -u) +} + +### +# Handles completions for flags that require, or optionally take, arguments. +# +# Usage: +# __yarn_flag_args +# +# Globals: +# cur +# prev +## +__yarn_flag_args() { + declare {arg,bool,dir,file,int,special}_flag_pattern + arg_flag_pattern="@($(tr ' ' '|' <<< "${arg_flags[*]}"))" + + # shellcheck disable=SC2053 + if [[ $prev != $arg_flag_pattern ]]; then + return 1 + fi + + bool_flag_pattern="@($(tr ' ' '|' <<< "${bool_arg_flags[*]}"))" + dir_flag_pattern="@($(tr ' ' '|' <<< "${dir_arg_flags[*]}"))" + file_flag_pattern="@($(tr ' ' '|' <<< "${file_arg_flags[*]}"))" + int_flag_pattern="@($(tr ' ' '|' <<< "${int_arg_flags[*]}"))" + special_flag_pattern="@($(tr ' ' '|' <<< "${special_arg_flags[*]}"))" + + case "$prev" in + $bool_flag_pattern) + COMPREPLY=($(compgen -W 'true false' -- "$cur")) + ;; + $dir_flag_pattern) + compopt -o dirnames + ;; + $file_flag_pattern) + compopt -o default -o filenames + ;; + $int_flag_pattern) + compopt -o nospace + COMPREPLY=($(compgen -W '{0..9}' -- "$cur")) + ;; + $special_flag_pattern) + case "$prev" in + --access) + COMPREPLY=($(compgen -W 'public restricted' -- "$cur")) + ;; + --groups) + COMPREPLY=($(compgen -W 'dependencies devDependencies optionalDependencies' -- "$cur")) + ;; + --level) + COMPREPLY=($(compgen -W 'info low moderate high critical' -- "$cur")) + ;; + --network-timeout) + compopt -o nospace + COMPREPLY=($(compgen -W '{1000..10000..1000}' -- "$cur")) + ;; + esac + ;; + esac + return 0 +} + +_yarn_add() { + ((depth++)) + flags=( + --audit -A + --dev -D + --exact -E + --optional -O + --peer -P + --tilde -T + --ignore-workspace-root-check -W + ) + return 1 +} + +_yarn_audit() { + ((depth++)) + flags=( + --groups + --level + --summary + ) + return 1 +} + +_yarn_autoclean() { + ((depth++)) + flags=( + --force -F + --init -I + ) + return 1 +} + +_yarn_cache() { + ((depth++)) + declare cmd + flags=( + --pattern + ) + subcommands=( + clean + dir + list + ) + __yarn_get_command + + case "$cmd" in + cache) + case "$cur" in + -*) + return 1 + ;; + *) + COMPREPLY=($(compgen -W "${subcommands[*]}" -- "$cur")) + return 0 + ;; + esac + ;; + *) + return 1 + ;; + esac +} + +_yarn_check() { + ((depth++)) + flags=( + --integrity + --verify-tree + ) + return 1 +} + +_yarn_config() { + ((depth++)) + declare cmd + declare subcommands=( + delete + get + list + set + ) + declare known_keys=( + ignore-optional + ignore-platform + ignore-scripts + init-author-email + init-author-name + init-author-url + init-license + init-version + no-progress + prefix + registry + save-prefix + user-agent + version-git-message + version-git-sign + version-git-tag + version-tag-prefix + ) + __yarn_get_command + + case "$cmd" in + get | delete) + case "$cur" in + -*) ;; + *) + if [[ $prev == @(get|delete) ]]; then + COMPREPLY=($(compgen -W "${known_keys[*]}" -- "$cur")) + return 0 + fi + ;; + esac + ;; + set) + case "$cur" in + -*) + flags=( + --global + ) + ;; + *) + if [[ $prev == set ]]; then + COMPREPLY=($(compgen -W "${known_keys[*]}" -- "$cur")) + return 0 + fi + ;; + esac + ;; + config) + case "$cur" in + -*) ;; + *) + COMPREPLY=($(compgen -W "${subcommands[*]}" -- "$cur")) + return 0 + ;; + esac + ;; + esac + return 1 +} + +_yarn_create() { + ((depth++)) + declare -i args + case "$cur" in + -*) ;; + *) + __yarn_count_args -d $depth + ((args == 0)) && return 0 + ;; + esac + return 1 +} + +_yarn_generate_lock_entry() { + ((depth++)) + flags=( + --resolved + --use-manifest + ) + return 1 +} + +_yarn_global() { + ((depth++)) + declare cmd cmdlist + flags=( + --latest + --prefix + ) + subcommands=( + add + bin + list + remove + upgrade + upgrade-interactive + ) + cmdlist="@($(tr ' ' '|' <<< "${subcommands[*]}"))" + + __yarn_get_command -d 3 + + case "$cur" in + -*) ;; + *) + case "$cmd" in + $cmdlist) + "_yarn_${cmd//-/_}" 2> /dev/null + return $? + ;; + global) + COMPREPLY=($(compgen -W "${subcommands[*]}" -- "$cur")) + return 0 + ;; + esac + ;; + esac + + return 1 +} + +_yarn_help() { + ((depth++)) + declare -i args + case "$cur" in + -*) ;; + *) + __yarn_count_args -d $depth + if ((args == 0)); then + COMPREPLY=($(compgen -W "${commands[*]}" -- "$cur")) + return 0 + fi + ;; + esac + return 1 +} + +_yarn_info() { + ((depth++)) + flags=( + --json + ) + declare standard_fields=( + author + bin + bugs + contributors + dependencies + description + devDependencies + dist-tags + engines + files + homepage + keywords + license + main + maintainers + name + optionalDependencies + peerDependencies + repository + version + versions + ) + + declare -i args + __yarn_count_args -d $depth + + case "$cur" in + -*) ;; + *) + case "$args" in + 0) + COMPREPLY=( + $(compgen -W " + $(__yarn_get_package_fields dependencies) + $(__yarn_get_package_fields devDependencies) + " -- "$cur") + ) + return 0 + ;; + 1) + COMPREPLY=($(compgen -W "${standard_fields[*]}" -- "$cur")) + return 0 + ;; + esac + ;; + esac + return 1 +} + +_yarn_init() { + ((depth++)) + flags=( + --yes -y + --private -p + --install -i + ) + return 1 +} + +_yarn_install() { + ((depth++)) + flags=( + --audit -A + ) + return 1 +} + +_yarn_licenses() { + ((depth++)) + declare cmd + subcommands=( + list + generate-disclaimer + ) + case "$cur" in + -*) ;; + *) + __yarn_get_command + case "$cmd" in + licenses) + COMPREPLY=($(compgen -W "${subcommands[*]}" -- "$cur")) + return 0 + ;; + esac + ;; + esac + return 1 +} + +_yarn_list() { + ((depth++)) + flags=( + --depth + --pattern + ) + return 1 +} + +_yarn_node() { + ((depth++)) + flags=( + --into + ) + return 1 +} + +_yarn_outdated() { + ((depth++)) + case "$cur" in + -*) ;; + *) + COMPREPLY=( + $(compgen -W " + $(__yarn_get_package_fields dependencies) + $(__yarn_get_package_fields devDependencies) + " -- "$cur") + ) + return 0 + ;; + esac + return 1 +} + +_yarn_owner() { + ((depth++)) + declare cmd + subcommands=( + add + list + remove + ) + __yarn_get_command + if [[ $cmd == owner ]]; then + case "$cur" in + -*) ;; + *) + COMPREPLY=($(compgen -W "${subcommands[*]}" -- "$cur")) + return 0 + ;; + esac + fi + return 1 +} + +_yarn_pack() { + ((depth++)) + flags=( + --filename -f + ) + return 1 +} + +_yarn_policies() { + ((depth++)) + declare standard_policies=( + latest + nightly + rc + ) + + declare -i args + __yarn_count_args -d $depth + + case "$cur" in + -*) ;; + *) + case "$args" in + 0) + COMPREPLY=($(compgen -W "${standard_policies[*]}" -- "$cur")) + return 0 + ;; + esac + ;; + esac + return 1 +} + +_yarn_publish() { + ((depth++)) + flags=( + --access + --major + --message + --minor + --new-version + --no-commit-hooks + --no-git-tag-version + --patch + --preid + --premajor + --preminor + --prepatch + --prerelease + --tag + ) + return 1 +} + +_yarn_remove() { + ((depth++)) + declare cmd dependencies devDependencies + flags=( + --ignore-workspace-root-check -W + ) + __yarn_get_command -d 1 + case "$cmd" in + global) + dependencies=$(__yarn_get_package_fields -g dependencies) + devDependencies='' + ;; + remove) + dependencies=$(__yarn_get_package_fields dependencies) + devDependencies=$(__yarn_get_package_fields devDependencies) + ;; + *) + return 1 + ;; + esac + case "$cur" in + -*) ;; + *) + COMPREPLY=($(compgen -W "$dependencies $devDependencies" -- "$cur")) + return 0 + ;; + esac + return 1 +} + +_yarn_run() { + ((depth++)) + declare cmd + subcommands=( + env + $(__yarn_get_package_fields scripts) + ) + __yarn_get_command + if [[ $cmd == run ]]; then + case "$cur" in + -*) ;; + *) + COMPREPLY=($(compgen -W "${subcommands[*]}" -- "$cur")) + return 0 + ;; + esac + fi + return 1 +} + +_yarn_tag() { + ((depth++)) + declare cmd + subcommands=( + add + list + remove + ) + __yarn_get_command + case "$cmd" in + tag) + case "$cur" in + -*) ;; + *) + COMPREPLY=($(compgen -W "${subcommands[*]}" -- "$cur")) + return 0 + ;; + esac + ;; + esac + return 1 +} + +_yarn_team() { + ((depth++)) + declare cmd + subcommands=( + add + create + destroy + list + remove + ) + __yarn_get_command + case "$cmd" in + team) + case "$cur" in + -*) ;; + *) + COMPREPLY=($(compgen -W "${subcommands[*]}" -- "$cur")) + return 0 + ;; + esac + ;; + esac + return 1 +} + +_yarn_unplug() { + ((depth++)) + flags=( + --clear + --clear-all + ) + case "$cur" in + -*) ;; + *) + COMPREPLY=( + $(compgen -W " + $(__yarn_get_package_fields dependencies) + $(__yarn_get_package_fields devDependencies) + " -- "$cur") + ) + return 0 + ;; + + esac + return 1 +} + +_yarn_upgrade() { + ((depth++)) + declare cmd dependencies devDependencies + flags=( + --audit -A + --caret -C + --exact -E + --latest -L + --pattern -P + --scope -S + --tilde -T + ) + __yarn_get_command -d 1 + case "$cmd" in + global) + dependencies=$(__yarn_get_package_fields -g dependencies) + devDependencies='' + ;; + upgrade) + dependencies=$(__yarn_get_package_fields dependencies) + devDependencies=$(__yarn_get_package_fields devDependencies) + ;; + *) + return 1 + ;; + esac + case "$cur" in + -*) ;; + *) + COMPREPLY=($(compgen -W "$dependencies $devDependencies" -- "$cur")) + return 0 + ;; + esac + return 1 +} + +_yarn_upgrade_interactive() { + ((depth++)) + flags=( + --caret -C + --exact -E + --latest + --scope -S + --tilde -T + ) + return 1 +} + +_yarn_version() { + ((depth++)) + flags=( + --major + --message + --minor + --new-version + --no-commit-hooks + --no-git-tag-version + --patch + --preid + --premajor + --preminor + --prepatch + --prerelease + ) + return 1 +} + +_yarn_workspace() { + ((depth++)) + declare -i args + declare workspaces_info + + case "$cur" in + -*) ;; + *) + __yarn_count_args + case "$args" in + [0-2]) + workspaces_info=$(yarn workspaces info -s 2> /dev/null) + if [[ -n $workspaces_info ]]; then + mapfile -t < <( + sed -n 's/^ \{2\}"\([^"]*\)": {$/\1/p' <<< "$workspaces_info" + ) + COMPREPLY=($(compgen -W "${MAPFILE[*]}" -- "$cur")) + fi + return 0 + ;; + 3) + COMPREPLY=($(compgen -W "${commands[*]}" -- "$cur")) + return 0 + ;; + *) + declare cmd + workspaces_info=$(yarn workspaces info -s 2> /dev/null) + + if [[ -n $workspaces_info ]]; then + PWD=$( + sed -n '/^ \{2\}"'"${COMP_WORDS[2]}"'": {$/,/^ \{2\}},\{0,1\}$/{ + s/^ \{4\}"location": "\([^"]*\)",$/\1/p + }' <<< "$workspaces_info" + ) + fi + + __yarn_get_command -d 3 + "_yarn_${cmd//-/_}" 2> /dev/null + return $? + ;; + esac + ;; + esac + return 1 +} + +_yarn_workspaces() { + ((depth++)) + declare cmd + subcommands=( + info + run + ) + __yarn_get_command -d 4 + case "$cmd" in + workspaces) + case "$cur" in + -*) ;; + *) + COMPREPLY=($(compgen -W "${subcommands[*]}" -- "$cur")) + return 0 + ;; + esac + ;; + info) + return 0 + ;; + run) + __yarn_run + return 0 + ;; + esac + return 1 +} + +_yarn_why() { + ((depth++)) + case "$cur" in + -*) ;; + ./*) + compopt -o filenames + ;; + *) + declare modules + modules=$(yarn list --depth 0 | sed -n 's/.* \([a-zA-Z0-9@].*\)@.*/\1/p') || return 1 + COMPREPLY=($(compgen -W "$modules" -- "$cur")) + return 0 + ;; + esac + return 1 +} + +_yarn_yarn() { + ((depth++)) + case "$cur" in + -*) ;; + *) + COMPREPLY=($(compgen -W "${commands[*]}" -- "$cur")) + return 0 + ;; + esac + return 1 +} + +_yarn() { + # shellcheck disable=SC2064 + trap " + PWD=$PWD + $(shopt -p extglob) + set +o pipefail + " RETURN + + shopt -s extglob + set -o pipefail + + declare cur cmd prev + declare -a words + declare -i cword counter=1 depth=1 + declare -ar commands=( + access + add + audit + autoclean + bin + cache + check + config + create + exec + generate-lock-entry + global + help + import + info + init + install + licenses + link + list + login + logout + node + outdated + owner + pack + policies + publish + remove + run + tag + team + unlink + unplug + upgrade + upgrade-interactive + version + versions + why + workspace + workspaces + $(__yarn_get_package_fields scripts) + ) + declare -a subcommands=() + + declare -ar bool_arg_flags=( + --emoji + --production --prod + --scripts-prepend-node-path + ) + declare -ar dir_arg_flags=( + --cache-folder + --cwd + --global-folder + --into + --link-folder + --modules-folder + --preferred-cache-folder + --prefix + ) + declare -ar file_arg_flags=( + --filename -f + --use-manifest + --use-yarnrc + ) + declare -ar int_arg_flags=( + --depth + --network-concurrency + ) + declare -ar special_arg_flags=( + --access + --groups + --level + --network-timeout + ) + declare -ar optional_arg_flags=( + --emoji + --prod + --production + --scripts-prepend-node-path + ) + declare -ar skipped_arg_flags=( + --https-proxy + --message + --mutex + --new-version + --otp + --pattern -P + --proxy + --registry + --resolved + --scope -S + --tag + ) + declare -ar arg_flags=( + "${bool_arg_flags[@]}" + "${dir_arg_flags[@]}" + "${file_arg_flags[@]}" + "${int_arg_flags[@]}" + "${special_arg_flags[@]}" + "${optional_arg_flags[@]}" + "${skipped_arg_flags[@]}" + ) + + declare -ar global_flags=( + --cache-folder + --check-files + --cwd + --disable-pnp + --emoji + --enable-pnp --pnp + --flat + --focus + --force + --frozen-lockfile + --global-folder + --har + --help -h + --https-proxy + --ignore-engines + --ignore-optional + --ignore-platform + --ignore-scripts + --json + --link-duplicates + --link-folder + --modules-folder + --mutex + --network-concurrency + --network-timeout + --no-bin-links + --no-default-rc + --no-lockfile + --non-interactive + --no-node-version-check + --no-progress + --offline + --otp + --prefer-offline + --preferred-cache-folder + --prod + --production + --proxy + --pure-lockfile + --registry + --scripts-prepend-node-path + --silent -s + --skip-integrity-check + --strict-semver + --update-checksums + --use-yarnrc + --verbose + --version -v + ) + declare -a flags=() + + COMPREPLY=() + if command -v _get_comp_words_by_ref > /dev/null; then + _get_comp_words_by_ref -n = -n @ -n : cur prev words cword + elif command -v _init_completion > /dev/null; then + _init_completion + fi + + __yarn_get_command -d 1 + + __yarn_flag_args || "_yarn_${cmd//-/_}" 2> /dev/null || __yarn_fallback + + if command -v __ltrim_colon_completions > /dev/null; then + __ltrim_colon_completions "$cur" + fi +} + +if [[ ${BASH_VERSINFO[0]} -ge 4 && ${BASH_VERSINFO[1]} -ge 4 ]]; then + complete -o nosort -F _yarn yarn +else + complete -F _yarn yarn +fi \ No newline at end of file