|
| 1 | +#/usr/bin/env bash |
| 2 | + |
| 3 | +_file_arguments() { |
| 4 | + local extensions="${1}" |
| 5 | + local reset=$(shopt -p globstar) |
| 6 | + shopt -s globstar |
| 7 | + |
| 8 | + if [[ -z "${cur_word}" ]]; then |
| 9 | + COMPREPLY=( $(compgen -fG -X "${extensions}" -- "${cur_word}") ); |
| 10 | + else |
| 11 | + COMPREPLY=( $(compgen -f -X "${extensions}" -- "${cur_word}") ); |
| 12 | + fi |
| 13 | + |
| 14 | + $reset |
| 15 | +} |
| 16 | + |
| 17 | +_long_short_completion() { |
| 18 | + local wordlist="${1}"; |
| 19 | + local short_options="${2}" |
| 20 | + |
| 21 | + [[ -z "${cur_word}" || "${cur_word}" =~ ^- ]] && { |
| 22 | + COMPREPLY=( $(compgen -W "${wordlist}" -- "${cur_word}")); |
| 23 | + return; |
| 24 | + } |
| 25 | + [[ "${cur_word}" =~ ^-[A-Za_z]+ ]] && { |
| 26 | + COMPREPLY=( $(compgen -W "${short_options}" -- "${cur_word}")); |
| 27 | + return; |
| 28 | + } |
| 29 | +} |
| 30 | + |
| 31 | +# loads the scripts block in package.json |
| 32 | +_read_scripts_in_package_json() { |
| 33 | + local package_json; |
| 34 | + local return_package_json |
| 35 | + local line=0; |
| 36 | + local working_dir="${PWD}"; |
| 37 | + |
| 38 | + for ((; line < ${#COMP_WORDS[@]}; line+=1)); do |
| 39 | + [[ "${COMP_WORDS[${line}]}" == "--cwd" ]] && working_dir="${COMP_WORDS[$((line + 1))]}"; |
| 40 | + done |
| 41 | + |
| 42 | + [[ -f "${working_dir}/package.json" ]] && package_json=$(<${working_dir}/package.json); |
| 43 | + |
| 44 | + [[ "${package_json}" =~ "\"scripts\""[[:space:]]*":"[[:space:]]*\{(.*)\} ]] && { |
| 45 | + local package_json_compreply; |
| 46 | + local matched="${BASH_REMATCH[@]:1}"; |
| 47 | + local scripts="${matched%%\}*}"; |
| 48 | + scripts="${scripts//@(\"|\')/}"; |
| 49 | + readarray -td, scripts <<<"${scripts}"; |
| 50 | + for completion in "${scripts[@]}"; do |
| 51 | + package_json_compreply+=( "${completion%:*}" ); |
| 52 | + done |
| 53 | + COMPREPLY+=( $(compgen -W "${package_json_compreply[*]}" -- "${cur_word}") ); |
| 54 | + } |
| 55 | + |
| 56 | + # when a script is passed as an option, do not show other scripts as part of the completion anymore |
| 57 | + local re_prev_script="(^| )${prev}($| )"; |
| 58 | + [[ |
| 59 | + ( "${COMPREPLY[*]}" =~ ${re_prev_script} && -n "${COMP_WORDS[2]}" ) || \ |
| 60 | + ( "${COMPREPLY[*]}" =~ ${re_comp_word_script} ) |
| 61 | + ]] && { |
| 62 | + local re_script=$(echo ${package_json_compreply[@]} | sed 's/[^ ]*/(&)/g'); |
| 63 | + local new_reply=$(echo "${COMPREPLY[@]}" | sed -E "s/$re_script//"); |
| 64 | + COMPREPLY=( $(compgen -W "${new_reply}" -- "${cur_word}") ); |
| 65 | + replaced_script="${prev}"; |
| 66 | + } |
| 67 | +} |
| 68 | + |
| 69 | + |
| 70 | +_subcommand_comp_reply() { |
| 71 | + local cur_word="${1}" |
| 72 | + local sub_commands="${2}" |
| 73 | + local regexp_subcommand="^[dbcriauh]"; |
| 74 | + [[ "${prev}" =~ ${regexp_subcommand} ]] && { |
| 75 | + COMPREPLY+=( $(compgen -W "${sub_commands}" -- "${cur_word}") ); |
| 76 | + } |
| 77 | +} |
| 78 | + |
| 79 | + |
| 80 | +_bun_completions() { |
| 81 | + declare -A GLOBAL_OPTIONS; |
| 82 | + declare -A PACKAGE_OPTIONS; |
| 83 | + declare -A PM_OPTIONS; |
| 84 | + |
| 85 | + local SUBCOMMANDS="dev bun create run install add remove upgrade completions discord help init pm x"; |
| 86 | + |
| 87 | + GLOBAL_OPTIONS[LONG_OPTIONS]="--use --cwd --bunfile --server-bunfile --config --disable-react-fast-refresh --disable-hmr --env-file --extension-order --jsx-factory --jsx-fragment --extension-order --jsx-factory --jsx-fragment --jsx-import-source --jsx-production --jsx-runtime --main-fields --no-summary --version --platform --public-dir --tsconfig-override --define --external --help --inject --loader --origin --port --dump-environment-variables --dump-limits --disable-bun-js"; |
| 88 | + GLOBAL_OPTIONS[SHORT_OPTIONS]="-c -v -d -e -h -i -l -u -p"; |
| 89 | + |
| 90 | + PACKAGE_OPTIONS[ADD_OPTIONS_LONG]="--development --optional"; |
| 91 | + PACKAGE_OPTIONS[ADD_OPTIONS_SHORT]="-d"; |
| 92 | + PACKAGE_OPTIONS[REMOVE_OPTIONS_LONG]=""; |
| 93 | + PACKAGE_OPTIONS[REMOVE_OPTIONS_SHORT]=""; |
| 94 | + |
| 95 | + PACKAGE_OPTIONS[SHARED_OPTIONS_LONG]="--config --yarn --production --frozen-lockfile --no-save --dry-run --force --cache-dir --no-cache --silent --verbose --global --cwd --backend --link-native-bins --help"; |
| 96 | + PACKAGE_OPTIONS[SHARED_OPTIONS_SHORT]="-c -y -p -f -g"; |
| 97 | + |
| 98 | + PM_OPTIONS[LONG_OPTIONS]="--config --yarn --production --frozen-lockfile --no-save --dry-run --force --cache-dir --no-cache --silent --verbose --no-progress --no-summary --no-verify --ignore-scripts --global --cwd --backend --link-native-bins --help" |
| 99 | + PM_OPTIONS[SHORT_OPTIONS]="-c -y -p -f -g" |
| 100 | + |
| 101 | + local cur_word="${COMP_WORDS[${COMP_CWORD}]}"; |
| 102 | + local prev="${COMP_WORDS[$(( COMP_CWORD - 1 ))]}"; |
| 103 | + |
| 104 | + case "${prev}" in |
| 105 | + help|--help|-h|-v|--version) return;; |
| 106 | + -c|--config) _file_arguments "!*.toml" && return;; |
| 107 | + --bunfile) _file_arguments "!*.bun" && return;; |
| 108 | + --server-bunfile) _file_arguments "!*.server.bun" && return;; |
| 109 | + --backend) |
| 110 | + case "${COMP_WORDS[1]}" in |
| 111 | + a|add|remove|rm|install|i) |
| 112 | + COMPREPLY=( $(compgen -W "clonefile copyfile hardlink clonefile_each_dir symlink" -- "${cur_word}") ); |
| 113 | + ;; |
| 114 | + esac |
| 115 | + return ;; |
| 116 | + --cwd|--public-dir) |
| 117 | + COMPREPLY=( $(compgen -d -- "${cur_word}" )); |
| 118 | + return;; |
| 119 | + --jsx-runtime) |
| 120 | + COMPREPLY=( $(compgen -W "automatic classic" -- "${cur_word}") ); |
| 121 | + return;; |
| 122 | + --target) |
| 123 | + COMPREPLY=( $(compgen -W "browser node bun" -- "${cur_word}") ); |
| 124 | + return;; |
| 125 | + -l|--loader) |
| 126 | + [[ "${cur_word}" =~ (:) ]] && { |
| 127 | + local cut_colon_forward="${cur_word%%:*}" |
| 128 | + COMPREPLY=( $(compgen -W "${cut_colon_forward}:jsx ${cut_colon_forward}:js ${cut_colon_forward}:json ${cut_colon_forward}:tsx ${cut_colon_forward}:ts ${cut_colon_forward}:css" -- "${cut_colon_forward}:${cur_word##*:}") ); |
| 129 | + } |
| 130 | + return;; |
| 131 | + esac |
| 132 | + |
| 133 | + case "${COMP_WORDS[1]}" in |
| 134 | + help|completions|--help|-h|-v|--version) return;; |
| 135 | + add|a) |
| 136 | + _long_short_completion \ |
| 137 | + "${PACKAGE_OPTIONS[ADD_OPTIONS_LONG]} ${PACKAGE_OPTIONS[ADD_OPTIONS_SHORT]} ${PACKAGE_OPTIONS[SHARED_OPTIONS_LONG]} ${PACKAGE_OPTIONS[SHARED_OPTIONS_SHORT]}" \ |
| 138 | + "${PACKAGE_OPTIONS[ADD_OPTIONS_SHORT]} ${PACKAGE_OPTIONS[SHARED_OPTIONS_SHORT]}" |
| 139 | + return;; |
| 140 | + remove|rm|i|install|link|unlink) |
| 141 | + _long_short_completion \ |
| 142 | + "${PACKAGE_OPTIONS[REMOVE_OPTIONS_LONG]} ${PACKAGE_OPTIONS[REMOVE_OPTIONS_SHORT]} ${PACKAGE_OPTIONS[SHARED_OPTIONS_LONG]} ${PACKAGE_OPTIONS[SHARED_OPTIONS_SHORT]}" \ |
| 143 | + "${PACKAGE_OPTIONS[REMOVE_OPTIONS_SHORT]} ${PACKAGE_OPTIONS[SHARED_OPTIONS_SHORT]}"; |
| 144 | + return;; |
| 145 | + create|c) |
| 146 | + COMPREPLY=( $(compgen -W "--force --no-install --help --no-git --verbose --no-package-json --open next react" -- "${cur_word}") ); |
| 147 | + return;; |
| 148 | + upgrade) |
| 149 | + COMPREPLY=( $(compgen -W "--version --cwd --help -v -h") ); |
| 150 | + return;; |
| 151 | + run) |
| 152 | + _file_arguments "!(*.@(js|ts|jsx|tsx|mjs|cjs)?($|))"; |
| 153 | + COMPREPLY+=( $(compgen -W "--version --cwd --help --silent -v -h" -- "${cur_word}" ) ); |
| 154 | + _read_scripts_in_package_json; |
| 155 | + return;; |
| 156 | + pm) |
| 157 | + _long_short_completion \ |
| 158 | + "${PM_OPTIONS[LONG_OPTIONS]} ${PM_OPTIONS[SHORT_OPTIONS]}"; |
| 159 | + COMPREPLY+=( $(compgen -W "bin ls cache hash hash-print hash-string" -- "${cur_word}") ); |
| 160 | + return;; |
| 161 | + *) |
| 162 | + local replaced_script; |
| 163 | + _long_short_completion \ |
| 164 | + "${GLOBAL_OPTIONS[*]}" \ |
| 165 | + "${GLOBAL_OPTIONS[SHORT_OPTIONS]}" |
| 166 | + |
| 167 | + _read_scripts_in_package_json; |
| 168 | + _subcommand_comp_reply "${cur_word}" "${SUBCOMMANDS}"; |
| 169 | + |
| 170 | + # determine if completion should be continued |
| 171 | + # when the current word is an empty string |
| 172 | + # the previous word is not part of the allowed completion |
| 173 | + # the previous word is not an argument to the last two option |
| 174 | + [[ -z "${cur_word}" ]] && { |
| 175 | + declare -A comp_reply_associative="( $(echo ${COMPREPLY[@]} | sed 's/[^ ]*/[&]=&/g') )"; |
| 176 | + [[ -z "${comp_reply_associative[${prev}]}" ]] && { |
| 177 | + local re_prev_prev="(^| )${COMP_WORDS[(( COMP_CWORD - 2 ))]}($| )"; |
| 178 | + local global_option_with_extra_args="--bunfile --server-bunfile --config --port --cwd --public-dir --jsx-runtime --platform --loader"; |
| 179 | + [[ |
| 180 | + ( -n "${replaced_script}" && "${replaced_script}" == "${prev}" ) || \ |
| 181 | + ( "${global_option_with_extra_args}" =~ ${re_prev_prev} ) |
| 182 | + ]] && return; |
| 183 | + unset COMPREPLY; |
| 184 | + } |
| 185 | + } |
| 186 | + return;; |
| 187 | + esac |
| 188 | + |
| 189 | +} |
| 190 | + |
| 191 | +complete -F _bun_completions bun |
0 commit comments