# bash_completion for conda. # # This was initially based on completion support for `fish`, but later extended # complete options for subcommands and files/dirs/paths as appropriate. # # Dynamic option lookup uses a cache that persists for the duration of the shell. # Updates to the conda command options are relatively rare, but there is a small chance # that this cache will hold incorrect/incomplete values. A restart of your shell will # fix this. # If this completion file is 'installed' under # # /etc/bash_completion.d/, # /usr/share/bash-completion/completions/, or # ~/.local/share/bash-completion/completions/, # # rather than being managed via the `conda shell.bash hook`, then this file may # be sourced before conda is setup. To support this we allow for a potential # late initialization of the CONDA_ROOT and CONDA_SOURCE environment # variables. # The extglob option is set by the bash_completion library anyway, # Setting it here to work around some rare edge cases. shopt -s extglob function __comp_conda_ensure_root() { if [[ -z "${CONDA_SOURCE-}" && -n "${CONDA_EXE-}" ]] ; then if [[ -n "${_CE_CONDA-}" && -n "${WINDIR-}" ]]; then CONDA_ROOT=$(\dirname "${CONDA_EXE}") else CONDA_ROOT=$(\dirname "${CONDA_EXE}") CONDA_ROOT=$(\dirname "${CONDA_ROOT}") fi \local script=" : from __future__ import print_function : import os : import conda : print(os.path.dirname(conda.__file__)) " script="${script// : /}" # don't assume an active base environment CONDA_SOURCE=$(conda activate base; python -c "$script") fi } function __comp_conda_commands () { # default core commands echo clean config create help info init install list package echo remove uninstall run search update upgrade # implied by conda shell function echo activate deactivate # check commands from full anaconda install for f in "$CONDA_SOURCE"/cli/main_*.py do # skip pip -- not a sub-command [[ "$f" == */main_pip.py ]] && continue if [[ "$f" =~ .*_([a-z]+)\.py$ ]]; then echo "${BASH_REMATCH[1]}" fi done # check extra pluggins for f in "$CONDA_ROOT"/bin/conda-* do if [[ -x "$f" && ! -d "$f" ]] then if [[ "$f" =~ .*/conda-(.*) ]]; then echo "${BASH_REMATCH[1]}" fi fi done } function __comp_conda_env_commands() { for f in "$CONDA_SOURCE"/../conda_env/cli/main_*.py do [[ "$f" == */main_vars.py ]] && continue if [[ "$f" =~ .*_([a-z]+)\.py$ ]]; then echo "${BASH_REMATCH[1]}" fi done } function __comp_conda_envs() { \local script=" : from __future__ import print_function : import json, os, sys : from os.path import isdir, join : print('\n'.join( : d for ed in json.load(sys.stdin)['envs_dirs'] if isdir(ed) : for d in os.listdir(ed) if isdir(join(ed, d))) : ) " script="${script// : /}" conda config --json --show envs_dirs | $CONDA_PYTHON_EXE -c "$script" } function __comp_conda_packages() { conda list | awk 'NR > 3 {print $1}' } function __comp_conda_cmds_str() { # get a list of commands, skipping options \local cmd \local -a cmds for cmd in "$@"; do case "$cmd" in -*) continue ;; *) cmds+=("$cmd") ;; esac done echo "${cmds[@]}" } # helper for debugging issues with the cache function __comp_conda_cache_dump() { for k in "${!__comp_conda_cache[@]}"; do printf "%s:\n" "$k" for w in ${__comp_conda_cache[$k]}; do printf "\t%s\n" "$w" done done } function __comp_conda_option_lookup() { \local word_list cmd_str cmd_key cmd_str=$1 # make a key to look up the cached result of the command help # (We should be able to just use $cmd_str, since spaces in an array key are fine, # but this produces an error with an empty cache. I'm not sure why though) cmd_key=${cmd_str// /_} if [[ -z "${__comp_conda_cache[$cmd_key]}" ]]; then # parse the output of command help to get completions word_list=$($cmd_str --help 2>&1 | _parse_help -) if [[ ${PIPESTATUS[0]} -eq 0 && -n $word_list ]]; then __comp_conda_cache[$cmd_key]=$word_list else # something went wrong, so abort completion attempt return 1 fi else word_list=${__comp_conda_cache[$cmd_key]} fi echo "$word_list" } # cache conda subcommand help lookups for the duration of the shell unset __comp_conda_cache declare -A __comp_conda_cache # If conda has not been fully setup/activated yet, some of the above functions may fail # and print error messages. This is not helpful during normal usage, so we discard all # error output by default. __comp_conda_ensure_root 2>/dev/null || : _comp_conda() { # shellcheck disable=SC2034 \local cur prev words cword _init_completion || return __comp_conda_ensure_root 2>/dev/null \local word_list cmd_str if [[ $cur == -* ]]; then # get the current list of commands as a string sans options cmd_str=$(__comp_conda_cmds_str "${words[@]}") word_list=$(__comp_conda_option_lookup "$cmd_str") else case "$prev" in conda) word_list=$(__comp_conda_commands 2>/dev/null) ;; env) word_list=$(__comp_conda_env_commands 2>/dev/null) ;; activate) if [[ $cur == */* ]] then _filedir -d # environment directories else word_list=$(__comp_conda_envs 2>/dev/null) fi ;; remove|uninstall|upgrade|update) word_list=$(__comp_conda_packages 2>/dev/null) ;; --name|--clone) word_list=$(__comp_conda_envs 2>/dev/null) ;; --*-file|--file|--which|convert) _filedir # filenames ;; --*-dir|--*-folder|--subdir|--prefix|--cwd|index) _filedir -d # directories ;; verify|debug) _filedir 'tar.bz2' # package paths and directories ;; build) _filedir 'tar.bz2' # package paths and directories word_list='purge purge-all' # special keywords ;; esac fi if [[ -n $word_list ]]; then # append completion suggestions to COMPREPLY mapfile -t -O "${#COMPREPLY[@]}" COMPREPLY < \ <(compgen -W "$word_list" -- "$cur") fi } && complete -F _comp_conda conda # vim: ft=sh