view contrib/bash_completion @ 2434:a2df85adface

http server: support persistent connections. only "hg serve" affected yet. http server running cgi script will not use persistent connections. support for fastcgi will help that. clients that support keepalive can use one tcp connection for all commands during clone and pull. this makes latency of binary search during pull much lower over wan. if server does not know content-length, it will force connection to close at end. right fix is to use chunked transfer-encoding but this is easier and does not hurt performance. only command that is affected is "changegroup" which is always last command during a pull.
author Vadim Gelfer <vadim.gelfer@gmail.com>
date Thu, 15 Jun 2006 12:55:58 -0700
parents 077a2da7f1de
children c995d68333cf
line wrap: on
line source

# bash completion for the Mercurial distributed SCM

# Docs:
#
# If you source this file from your .bashrc, bash should be able to
# complete a command line that uses hg with all the available commands
# and options and sometimes even arguments.
#
# Mercurial allows you to define additional commands through extensions.
# Bash should be able to automatically figure out the name of these new
# commands and their options.  If you also want to tell it how to
# complete non-option arguments, see below for how to define an
# _hg_cmd_foo function.
#
#
# Notes about completion for specific commands:
#
# - the completion function for the email command from the patchbomb
#   extension will try to call _hg_emails to get a list of e-mail
#   addresses.  It's up to the user to define this function.  For
#   example, put the addresses of the lists that you usually patchbomb
#   in ~/.patchbomb-to and the addresses that you usually use to send
#   the patchbombs in ~/.patchbomb-from and use something like this:
#
#      _hg_emails()
#      {
#          if [ -r ~/.patchbomb-$1 ]; then
#              cat ~/.patchbomb-$1
#          fi
#      }
# 
#
# Writing completion functions for additional commands:
#
# If it exists, the function _hg_cmd_foo will be called without
# arguments to generate the completion candidates for the hg command
# "foo".
#
# In addition to the regular completion variables provided by bash,
# the following variables are also set:
# - $hg - the hg program being used (e.g. /usr/bin/hg)
# - $cmd - the name of the hg command being completed
# - $cmd_index - the index of $cmd in $COMP_WORDS
# - $cur - the current argument being completed
# - $prev - the argument before $cur
# - $global_args - "|"-separated list of global options that accept
#                  an argument (e.g. '--cwd|-R|--repository')
# - $canonical - 1 if we canonicalized $cmd before calling the function
#                0 otherwise
# 

shopt -s extglob

_hg_commands()
{
    local commands
    commands="$("$hg" debugcomplete "$cur" 2>/dev/null)" || commands=""
    COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$commands' -- "$cur"))
}

_hg_paths()
{
    local paths="$("$hg" paths 2>/dev/null | sed -e 's/ = .*$//')"
    COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$paths' -- "$cur"))
}

_hg_repos()
{
    local i
    for i in $(compgen -d -- "$cur"); do
	test ! -d "$i"/.hg || COMPREPLY=(${COMPREPLY[@]:-} "$i")
    done
}

_hg_status()
{
    local files="$("$hg" status -n$1 . 2>/dev/null)"
    COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$files' -- "$cur"))
}

_hg_tags()
{
    local tags="$("$hg" tags -q 2>/dev/null)"
    local IFS=$'\n'
    COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$tags' -- "$cur"))
}

# this is "kind of" ugly...
_hg_count_non_option()
{
    local i count=0
    local filters="$1"

    for ((i=1; $i<=$COMP_CWORD; i++)); do
	if [[ "${COMP_WORDS[i]}" != -* ]]; then
	    if [[ ${COMP_WORDS[i-1]} == @($filters|$global_args) ]]; then
		continue
	    fi
	    count=$(($count + 1))
	fi
    done

    echo $(($count - 1))
}

_hg()
{
    local cur prev cmd cmd_index opts i
    # global options that receive an argument
    local global_args='--cwd|-R|--repository'
    local hg="$1"

    COMPREPLY=()
    cur="$2"
    prev="$3"

    # searching for the command
    # (first non-option argument that doesn't follow a global option that
    #  receives an argument)
    for ((i=1; $i<=$COMP_CWORD; i++)); do
	if [[ ${COMP_WORDS[i]} != -* ]]; then
	    if [[ ${COMP_WORDS[i-1]} != @($global_args) ]]; then
		cmd="${COMP_WORDS[i]}"
		cmd_index=$i
		break
	    fi
	fi
    done

    if [[ "$cur" == -* ]]; then
	opts=$("$hg" debugcomplete --options "$cmd" 2>/dev/null)

	COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$opts' -- "$cur"))
	return
    fi

    # global options
    case "$prev" in
	-R|--repository)
	    _hg_repos
	    return
	;;
	--cwd)
	    # Stick with default bash completion
	    return
	;;
    esac

    if [ -z "$cmd" ] || [ $COMP_CWORD -eq $i ]; then
	_hg_commands
	return
    fi

    # try to generate completion candidates for whatever command the user typed
    local help
    local canonical=0
    if _hg_command_specific; then
	return
    fi

    # canonicalize the command name and try again
    help=$("$hg" help "$cmd" 2>/dev/null)
    if [ $? -ne 0 ]; then
	# Probably either the command doesn't exist or it's ambiguous
	return
    fi
    cmd=${help#hg }
    cmd=${cmd%%[$' \n']*}
    canonical=1
    _hg_command_specific
}

_hg_command_specific()
{
    if [ "$(type -t "_hg_cmd_$cmd")" = function ]; then
	"_hg_cmd_$cmd"
	return 0
    fi

    if [ "$cmd" != status ] && [ "$prev" = -r ] || [ "$prev" == --rev ]; then
	if [ $canonical = 1 ]; then
	    _hg_tags
	    return 0
	elif [[ status != "$cmd"* ]]; then
	    _hg_tags
	    return 0
	else
	    return 1
	fi
    fi

    case "$cmd" in
	help)
	    _hg_commands
	;;
	export|manifest|update)
	    _hg_tags
	;;
	pull|push|outgoing|incoming)
	    _hg_paths
	    _hg_repos
	;;
	paths)
	    _hg_paths
	;;
	add)
	    _hg_status "u"
	;;
	commit)
	    _hg_status "mar"
	;;
	remove)
	    _hg_status "d"
	;;
	forget)
	    _hg_status "a"
	;;
	diff)
	    _hg_status "mar"
	;;
	revert)
	    _hg_status "mard"
	;;
	clone)
	    local count=$(_hg_count_non_option)
	    if [ $count = 1 ]; then
		_hg_paths
	    fi
	    _hg_repos
	;;
	debugindex|debugindexdot)
	    COMPREPLY=(${COMPREPLY[@]:-} $(compgen -f -X "!*.i" -- "$cur"))
	;;
	debugdata)
	    COMPREPLY=(${COMPREPLY[@]:-} $(compgen -f -X "!*.d" -- "$cur"))
	;;
	*)
	    return 1
	;;
    esac

    return 0
}

complete -o bashdefault -o default -F _hg hg 2>/dev/null \
    || complete -o default -F _hg hg


# Completion for commands provided by extensions

# mq
_hg_ext_mq_patchlist()
{
    local patches=$("$hg" $1 2>/dev/null)
    COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$patches' -- "$cur"))
}

_hg_ext_mq_queues()
{
    local root=$("$hg" root 2>/dev/null)
    local n
    for n in $(cd "$root"/.hg && compgen -d -- "$cur"); do
	# I think we're usually not interested in the regular "patches" queue
	# so just filter it.
	if [ "$n" != patches ] && [ -e "$root/.hg/$n/series" ]; then
	    COMPREPLY=(${COMPREPLY[@]:-} "$n")
	fi
    done
}

_hg_cmd_qpop()
{
    if [[ "$prev" = @(-n|--name) ]]; then
	_hg_ext_mq_queues
	return
    fi
    _hg_ext_mq_patchlist qapplied
}

_hg_cmd_qpush()
{
    if [[ "$prev" = @(-n|--name) ]]; then
	_hg_ext_mq_queues
	return
    fi
    _hg_ext_mq_patchlist qunapplied
}

_hg_cmd_qdelete()
{
    _hg_ext_mq_patchlist qseries
}

_hg_cmd_qsave()
{
    if [[ "$prev" = @(-n|--name) ]]; then
	_hg_ext_mq_queues
	return
    fi
}

_hg_cmd_strip()
{
    _hg_tags
}

_hg_cmd_qcommit()
{
    local root=$("$hg" root 2>/dev/null)
    # this is run in a sub-shell, so we can't use _hg_status
    local files=$(cd "$root/.hg/patches" 2>/dev/null &&
                  "$hg" status -nmar 2>/dev/null)
    COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$files' -- "$cur"))
}


# hbisect
_hg_cmd_bisect()
{
    local i subcmd

    # find the sub-command
    for ((i=cmd_index+1; i<=COMP_CWORD; i++)); do
	if [[ ${COMP_WORDS[i]} != -* ]]; then
	    if [[ ${COMP_WORDS[i-1]} != @($global_args) ]]; then
		subcmd="${COMP_WORDS[i]}"
		break
	    fi
	fi
    done

    if [ -z "$subcmd" ] || [ $COMP_CWORD -eq $i ] || [ "$subcmd" = help ]; then
	COMPREPLY=(${COMPREPLY[@]:-} 
		   $(compgen -W 'bad good help init next reset' -- "$cur"))
	return
    fi

    case "$subcmd" in
	good|bad)
	    _hg_tags
	    ;;
    esac

    return
}


# patchbomb
_hg_cmd_email()
{
    case "$prev" in
	-c|--cc|-t|--to|-f|--from)
	    # we need an e-mail address. let the user provide a function 
	    # to get them
	    if [ "$(type -t _hg_emails)" = function ]; then
		local arg=to
		if [[ "$prev" == @(-f|--from) ]]; then
		    arg=from
		fi
		local addresses=$(_hg_emails $arg)
		COMPREPLY=(${COMPREPLY[@]:-}
			   $(compgen -W '$addresses' -- "$cur"))
	    fi
	    return
	    ;;
	-m|--mbox)
	    # fallback to standard filename completion
	    return
	    ;;
	-s|--subject)
	    # free form string
	    return
	    ;;
    esac

    _hg_tags
    return
}


# gpg
_hg_cmd_sign()
{
    _hg_tags
}