# GNU Guix --- Functional package management for GNU # Copyright © 2015, 2016, 2017, 2018, 2019, 2020, 2021 Ludovic Courtès # Copyright © 2021 Tobias Geerinck-Rice # # This file is part of GNU Guix. # # GNU Guix is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or (at # your option) any later version. # # GNU Guix is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNU Guix. If not, see . # Bash completion for Guix commands. declare _guix_available_packages declare _guix_commands _guix_complete_command () { local word_at_point="${COMP_WORDS[$COMP_CWORD]}" if [ -z "$_guix_commands" ] then # Cache the list of commands to speed things up. _guix_commands="$(${COMP_WORDS[0]} --help 2> /dev/null \ | grep '^ ' \ | sed '-es/^ *\([a-z-]\+\).*$/\1/g')" fi COMPREPLY+=($(compgen -W "$_guix_commands" -- "$word_at_point")) } _guix_complete_subcommand () { local command="${COMP_WORDS[1]}" local subcommands="$(${COMP_WORDS[0]} $command --help 2> /dev/null \ | grep '^ [a-z]' \ | sed -e's/^ \+\([a-z-]\+\).*$/\1/g')" COMPREPLY+=($(compgen -W "$subcommands" -- "${COMP_WORDS[$COMP_CWORD]}")) } _guix_complete_available_package () { local prefix="$1" if [ -z "$_guix_available_packages" ] then # Cache the complete list because it rarely changes and makes # completion much faster. _guix_available_packages="$(${COMP_WORDS[0]} package -A 2> /dev/null \ | cut -f1)" fi COMPREPLY+=($(compgen -W "$_guix_available_packages" -- "$prefix")) } _guix_complete_installed_package () { # Here we do not cache the list of installed packages because that # may change over time and the list is relatively small anyway. local prefix="$1" local packages="$(${COMP_WORDS[0]} package -I "^$prefix" 2> /dev/null \ | cut -f1)" COMPREPLY+=($(compgen -W "$packages" -- "$prefix")) } _guix_complete_option () { local command="${COMP_WORDS[$1]}" local subcommand="${COMP_WORDS[$(($1 + 1))]}" if [ $1 -eq 0 ] then command="" subcommand="" elif _guix_is_option "$subcommand" then subcommand="" fi local options="$(${COMP_WORDS[0]} $command $subcommand --help 2> /dev/null \ | grep '^ \+-' \ | sed -e's/^.*--\([a-zA-Z0-9_-]\+\)\(=\?\).*/--\1\2/g')" compopt -o nospace COMPREPLY+=($(compgen -W "$options" -- "$2")) } _guix_is_option () { case "$1" in -*) true ;; *) false ;; esac } _guix_is_removing () { local word local result="false" for word in ${COMP_WORDS[*]} do case "$word" in --remove|--remove=*|-r) result=true break ;; esac done $result } _guix_is_dash_f () { [ "${COMP_WORDS[$COMP_CWORD - 1]}" = "-f" ] \ || { case "${COMP_WORDS[$COMP_CWORD]}" in --file=*|--install-from-file=*) true;; *) false;; esac } } _guix_is_dash_l () { [ "${COMP_WORDS[$COMP_CWORD - 1]}" = "-l" ] \ || { case "${COMP_WORDS[$COMP_CWORD]}" in --load=*) true;; *) false;; esac } } _guix_is_dash_L () { [ "${COMP_WORDS[$COMP_CWORD - 1]}" = "-L" ] \ || { case "${COMP_WORDS[$COMP_CWORD]}" in --load-path=*) true;; *) false;; esac } } _guix_is_dash_m () { [ "${COMP_WORDS[$COMP_CWORD - 1]}" = "-m" ] \ || { case "${COMP_WORDS[$COMP_CWORD]}" in --manifest=*) true;; *) false;; esac } } _guix_is_dash_C () { [ "${COMP_WORDS[$COMP_CWORD - 1]}" = "-C" ] \ || { case "${COMP_WORDS[$COMP_CWORD]}" in --channels=*) true;; *) false;; esac } } _guix_is_dash_p () { [ "${COMP_WORDS[$COMP_CWORD - 1]}" = "-p" ] \ || { case "${COMP_WORDS[$COMP_CWORD]}" in --profile=*) true;; *) false;; esac } } _guix_complete_file () { # Let Readline complete file names. compopt -o default COMPREPLY=() } _guix_complete_available_package_or_store_file () { _guix_complete_available_package "$@" # The current _guix_complete_file implementation doesn't compose (append to # COMPREPLY), so we suggest file names only if no package names matched. if [[ -z "$COMPREPLY" ]] then _guix_complete_file # TODO: restrict to store files fi } _guix_complete_pid () { local pids="$(cd /proc; echo [0-9]*)" COMPREPLY+=($(compgen -W "$pids" -- "$1")) } _guix_complete () { local word_count=${#COMP_WORDS[*]} local word_at_point="${COMP_WORDS[$COMP_CWORD]}" # Find the innermost command at point, e.g. "build" in the case of # "guix time-machine OPTIONS -- build" -- but "time-machine" if # point is moved before "build". local command_index=0 local command local word_index=0 local word local expect_command="true" while [[ $((++word_index)) -le COMP_CWORD ]] do word="${COMP_WORDS[$word_index]}" if $expect_command then command_index=$word_index command="$word" expect_command="false" continue fi if [[ "$word" = "--" ]] then case "$command" in environment|shell) break ;; time-machine) expect_command="true" ;; esac fi done case $COMP_CWORD in $command_index) _guix_complete_command _guix_complete_option 0 "$word_at_point" ;; *) if [[ "$command" = "package" ]] then if _guix_is_dash_L || _guix_is_dash_m || _guix_is_dash_p || _guix_is_dash_f then _guix_complete_file elif _guix_is_removing then _guix_complete_installed_package "$word_at_point" else _guix_complete_available_package "$word_at_point" fi elif [[ "$command" = "install" ]] then if _guix_is_dash_L || _guix_is_dash_m || _guix_is_dash_p then _guix_complete_file else _guix_complete_available_package "$word_at_point" fi elif [[ "$command" = "upgrade" || "$command" = "remove" ]] then if _guix_is_dash_L || _guix_is_dash_m || _guix_is_dash_p then _guix_complete_file else _guix_complete_installed_package "$word_at_point" fi elif [[ "$command" = "build" ]] then if _guix_is_dash_L || _guix_is_dash_m || _guix_is_dash_f then _guix_complete_file else _guix_complete_available_package_or_store_file "$word_at_point" fi elif [[ "$command" = "environment" || "$command" = "shell" ]] then if _guix_is_dash_f && [[ "$command" = "shell" ]] then # The otherwise identical ‘guix environment’ lacks the ‘-f’ option. _guix_complete_file elif _guix_is_dash_L || _guix_is_dash_m || _guix_is_dash_p || _guix_is_dash_l then _guix_complete_file elif _guix_is_option "$word_at_point" then _guix_complete_option "$command_index" "$word_at_point" else _guix_complete_available_package "$word_at_point" fi elif [[ "$command" = "download" || "$command" = "gc" || "$command" = "hash" ]] then _guix_complete_file elif [[ "$command" = "size" ]] then _guix_complete_available_package_or_store_file "$word_at_point" elif [[ "$command" = "system" ]] then case $((COMP_CWORD - command_index)) in 1) _guix_complete_subcommand;; *) _guix_complete_file;; # TODO: restrict to *.scm esac elif [[ "$command" = "pull" ]] then if _guix_is_dash_C || _guix_is_dash_p then _guix_complete_file fi elif [[ "$command" = "time-machine" ]] then if _guix_is_dash_C then _guix_complete_file else _guix_complete_option "$command_index" "$word_at_point" fi elif [[ "$command" = "container" ]] then case $((COMP_CWORD - command_index)) in 1) _guix_complete_subcommand;; 2) _guix_complete_pid "$word_at_point";; *) _guix_complete_file;; esac elif [[ "$command" = "import" ]] then _guix_complete_subcommand elif [[ "$command" = "weather" ]] then if _guix_is_dash_m then _guix_complete_file else _guix_complete_available_package "$word_at_point" fi else _guix_complete_available_package "$word_at_point" fi ;; esac if [[ -z "$COMPREPLY" && COMP_CWORD -gt command_index ]] && _guix_is_option "$word_at_point" then _guix_complete_option "$command_index" "$word_at_point" fi } complete -F _guix_complete guix