#!/usr/bin/env bash # # Summary: Display help for a command # # Usage: pyenv help [--usage] COMMAND # # Parses and displays help contents from a command's source file. # # A command is considered documented if it starts with a comment block # that has a `Summary:' or `Usage:' section. Usage instructions can # span multiple lines as long as subsequent lines are indented. # The remainder of the comment block is displayed as extended # documentation. set -e [ -n "$PYENV_DEBUG" ] && set -x # Provide pyenv completions if [ "$1" = "--complete" ]; then echo --usage exec pyenv-commands fi command_path() { local command="$1" command -v pyenv-"$command" || command -v pyenv-sh-"$command" || true } extract_initial_comment_block() { LC_ALL= \ LC_CTYPE=C \ sed -ne " /^#/ !{ q } s/^#$/# / /^# / { s/^# // p } " } collect_documentation() { # `tail` prevents "broken pipe" errors due to `head` closing the pipe without reading everything # https://superuser.com/questions/554855/how-can-i-fix-a-broken-pipe-error/642932#642932 $(type -P gawk awk | tail -n +1 | head -n1) ' /^Summary:/ { summary = substr($0, 10) next } /^Usage:/ { reading_usage = 1 usage = usage "\n" $0 next } /^( *$| )/ && reading_usage { usage = usage "\n" $0 next } { reading_usage = 0 help = help "\n" $0 } function escape(str) { gsub(/[`\\$"]/, "\\\\&", str) return str } function trim(str) { sub(/^\n*/, "", str) sub(/\n*$/, "", str) return str } END { if (usage || summary) { print "summary=\"" escape(summary) "\"" print "usage=\"" escape(trim(usage)) "\"" print "help=\"" escape(trim(help)) "\"" } } ' } documentation_for() { local filename filename="$(command_path "$1")" if [ -n "$filename" ]; then extract_initial_comment_block < "$filename" | collect_documentation fi } print_summary() { local command="$1" local summary usage help eval "$(documentation_for "$command")" if [ -n "$summary" ]; then printf " %-9s %s\n" "$command" "$summary" fi } print_summaries() { for command; do print_summary "$command" done } print_help() { local command="$1" local summary usage help eval "$(documentation_for "$command")" [ -n "$help" ] || help="$summary" if [ -n "$usage" ] || [ -n "$summary" ]; then if [ -n "$usage" ]; then echo "$usage" else echo "Usage: pyenv ${command}" fi if [ -n "$help" ]; then echo echo "$help" echo fi else echo "Sorry, this command isn't documented yet." >&2 return 1 fi } print_usage() { local command="$1" local summary usage help eval "$(documentation_for "$command")" [ -z "$usage" ] || echo "$usage" } unset usage if [ "$1" = "--usage" ]; then usage="1" shift fi if [ -z "$1" ] || [ "$1" == "pyenv" ]; then echo "Usage: pyenv []" [ -z "$usage" ] || exit echo echo "Some useful pyenv commands are:" print_summaries $(exec pyenv-commands | sort -u) echo echo "See \`pyenv help ' for information on a specific command." echo "For full documentation, see: https://github.com/pyenv/pyenv#readme" else command="$1" if [ -n "$(command_path "$command")" ]; then if [ -n "$usage" ]; then print_usage "$command" else print_help "$command" fi else echo "pyenv: no such command \`$command'" >&2 exit 1 fi fi