Blissify
diff --git a/bash_completion/repo.bash b/bash_completion/repo.bash
new file mode 100755
index 0000000..9ae5e00
--- /dev/null
+++ b/bash_completion/repo.bash
@@ -0,0 +1,655 @@
+# -*- mode: sh; -*-
+
+declare -A CMD_HANDLERS
+CMD_HANDLERS=(
+    ["init"]=_repo_init
+    ["help"]=_repo_help
+    ["abandon"]=_repo_abandon
+    ["branch"]=_repo_branch
+    ["branches"]=_repo_branches
+    ["checkout"]=_repo_checkout
+    ["cherry-pick"]=_repo_cherry_pick
+    ["diff"]=_repo_diff
+    ["download"]=_repo_download
+    ["forall"]=_repo_forall
+    ["grep"]=_repo_grep
+    ["list"]=_repo_list
+    ["prune"]=_repo_prune
+    ["rebase"]=_repo_rebase
+    ["selfupdate"]=_repo_selfupdate
+    ["smartsync"]=_repo_smartsync
+    ["stage"]=_repo_stage
+    ["start"]=_repo_start
+    ["status"]=_repo_status
+    ["sync"]=_repo_sync
+    ["upload"]=_repo_upload
+    ["version"]=_repo_version
+)
+
+# To be populated by command handlers.
+declare -a OPTIONS
+declare -A ARG_OPTIONS
+
+declare cur
+declare prev
+
+_init_cur_prev() {
+    cur=$(_get_cword "=")
+    prev=$(_get_cword "=" 1)
+
+    _split_longopt
+}
+
+_find_repo() {
+    local dir=$(pwd)
+    local found=1
+
+    while [ "${dir}" != / ]
+    do
+        if [ -e "${dir}/.repo/repo/main.py" ]
+        then
+            found=0
+            break
+        fi
+
+        dir=$(cd "${dir}/.." && pwd)
+    done
+
+    if [ ${found} -eq 0 ]
+    then
+        echo "${dir}"
+    fi
+}
+
+_is_repo_dir() {
+    local repo_root=$(_find_repo)
+
+    [ -n "${repo_root}" ]
+}
+
+_gen_comps() {
+    local completions="$1"
+    local suffix="${2:- }"
+
+    local -i i
+    local -a tmp=( $(compgen -W "${completions}" -- ${cur}) )
+
+    for (( i=0; i < ${#tmp[*]}; i++ ))
+    do
+        tmp[$i]="${tmp[$i]}${suffix}"
+    done
+
+    COMPREPLY=(
+        "${COMPREPLY[@]}"
+        "${tmp[@]}"
+    )
+}
+
+_strip_colors () {
+    # taken from http://goo.gl/7KlLZ
+    sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]//g"
+}
+
+_no_completion() {
+    true
+}
+
+_command_completion() {
+    local cmds
+
+    if _is_repo_dir
+    then
+        cmds=("abandon" "branch" "branches" "checkout" "cherry-pick" "diff"
+            "download" "forall" "grep" "help" "init" "list" "prune" "rebase"
+            "selfupdate" "smartsync" "stage" "start" "status" "sync"
+            "upload" "version")
+    else
+        cmds=("help" "init")
+    fi
+
+    _gen_comps "${cmds[*]}"
+}
+
+_branch_completion() {
+    local raw_branches
+
+    # separate statement required to be able to access exit code
+    raw_branches=$(repo branches 2>/dev/null)
+
+    if [ $? -eq 0 ]
+    then
+        local branches=$(
+            echo "${raw_branches}" |
+            _strip_colors | awk 'BEGIN { FS="|" } { print $1 }' | cut -c 3-
+        )
+
+        _gen_comps "${branches}"
+    fi
+}
+
+_dir_completion() {
+    _filedir -d
+}
+
+_project_completion() {
+    local repo_root=$(_find_repo)
+
+    if [ -n "${repo_root}" -a -f "${repo_root}/.repo/project.list" ]
+    then
+        local projects=$(cat "${repo_root}/.repo/project.list")
+        _gen_comps "${projects}"
+    fi
+}
+
+_manifest_completion() {
+    local repo_root=$(_find_repo)
+
+    if [ -n "${repo_root}" ]
+    then
+        local manifests_dir="${repo_root}/.repo/manifests"
+        local git_dir="${manifests_dir}/.git"
+        local candidates
+
+        manifests=$(
+            git --git-dir "${git_dir}" ls-files "*.xml" 2>/dev/null)
+
+        if [ $? -eq 0 ]
+        then
+            _gen_comps "${manifests}"
+        fi
+    fi
+}
+
+_path_cmd_completion() {
+    _gen_comps "$(compgen -c ${cur})"
+}
+
+_is_option() {
+    local opt="$1"
+
+    [[ "${opt}" == -* ]]
+}
+
+_is_long_option() {
+    local opt="$1"
+
+    [[ "${opt}" == --* ]]
+}
+
+_expects_arg() {
+    local opt="$1"
+
+    if [[ ${ARG_OPTIONS[$opt]} ]]
+    then
+        return 0
+    else
+        return 1
+    fi
+}
+
+_handle_options() {
+    if _expects_arg "${prev}"
+    then
+        local handler=${ARG_OPTIONS[$prev]}
+        eval ${handler} "${cur}"
+    elif _is_option "${cur}"
+    then
+        _gen_comps "${OPTIONS[*]}"
+
+        local arg_short
+        local arg_long
+
+        for opt in "${!ARG_OPTIONS[@]}"
+        do
+            if _is_long_option "${opt}"
+            then
+                arg_long="${arg_long} ${opt}"
+            else
+                arg_short="${arg_short} ${opt}"
+            fi
+        done
+
+        _gen_comps "${arg_short}"
+        _gen_comps "${arg_long}" "="
+    else
+        return 1
+    fi
+
+    return 0
+}
+
+_is_known_shortopt() {
+    local needle="$1"
+
+    for opt in ${OPTIONS[@]}
+    do
+        if [ "${opt}" = "${needle}" ]
+        then
+            return 0
+        fi
+    done
+
+    return 1
+}
+
+_is_known_longopt() {
+    local needle="$1"
+
+    [[ ${ARG_OPTIONS[$1]} ]]
+}
+
+_arg_index() {
+    local -i i=2               # skip repo and command
+    local -i ix=0
+
+    while [ ${i} -lt ${COMP_CWORD} ]
+    do
+        if _is_known_shortopt "${COMP_WORDS[i]}"
+        then
+            i+=1
+        elif _is_known_longopt "${COMP_WORDS[i]}"
+        then
+            i+=2
+        elif _is_option "${COMP_WORDS[i]}"
+        then
+            i+=1
+        else
+            i+=1
+            ix+=1
+        fi
+    done
+
+    eval $1="${ix}"
+}
+
+_when_ix() {
+    local ix="$1"
+    local completion="$2"
+
+    _arg_index arg_ix
+
+    if [ ${arg_ix} -eq ${ix} ]
+    then
+        ${completion}
+        return 0
+    else
+        return 1
+    fi
+}
+
+_when_first() {
+    _when_ix 0 "$1"
+}
+
+_when_even() {
+    local completion="$1"
+
+    _arg_index arg_ix
+
+    if [ $(( ${arg_ix} % 2 )) -eq 0 ]
+    then
+        ${completion}
+        return 0
+    else
+        return 1
+    fi
+}
+
+_cmp_opts() {
+    local opt="$1"
+    local word="$2"
+
+    if _is_option "${opt}" && ! _is_long_option "${opt}"
+    then
+        [ "${word}" == "${opt}" ]
+    else
+        [[ "${word}" == "${opt}"=* || "${word}" == "${opt}" ]]
+    fi
+}
+
+_before() {
+    local completion="$1"
+    local words
+
+    shift
+
+    _get_comp_words_by_ref -n = words
+
+    for word in "${words[@]}"
+    do
+        for needle in "$@"
+        do
+            if _cmp_opts "${needle}" "${word}"
+            then
+                return 1
+            fi
+        done
+    done
+
+    ${completion}
+    return 0
+}
+
+_repo_init() {
+    OPTIONS=(
+        "-h" "--help"
+        "-q" "--quite"
+        "--mirror"
+        "--no-repo-verify"
+    )
+
+    ARG_OPTIONS=(
+        ["-u"]=_no_completion
+        ["--manifest-url"]=_no_completion
+        ["-b"]=_no_completion
+        ["--manifest-branch"]=_no_completion
+        ["-m"]=_manifest_completion
+        ["--manifest-name"]=_manifest_completion
+        ["--reference"]=_dir_completion
+        ["--repo-url"]=_no_completion
+        ["--repo-branch"]=_no_completion
+    )
+
+    _handle_options
+}
+
+_repo_help() {
+    OPTIONS=(
+        "-a" "--all"
+        "-h" "--help"
+    )
+
+    ARG_OPTIONS=()
+
+    _handle_options || _when_first _command_completion
+}
+
+_repo_abandon() {
+    OPTIONS=(
+        "-h" "--help"
+    )
+
+    ARG_OPTIONS=()
+
+    _handle_options || _when_first _branch_completion || _project_completion
+}
+
+_repo_branch() {
+    OPTIONS=(
+        "-h" "--help"
+    )
+
+    ARG_OPTIONS=()
+
+    _handle_options
+}
+
+_repo_branches() {
+    OPTIONS=(
+        "-h" "--help"
+    )
+
+    ARG_OPTIONS=()
+
+    _handle_options
+}
+
+_repo_checkout() {
+    OPTIONS=(
+        "-h" "--help"
+    )
+
+    ARG_OPTIONS=()
+
+    _handle_options || _when_first _branch_completion || _project_completion
+}
+
+_repo_cherry_pick() {
+    OPTIONS=(
+        "-h" "--help"
+    )
+
+    ARG_OPTIONS=()
+
+    _handle_options
+}
+
+_repo_diff() {
+    OPTIONS=(
+        "-h" "--help"
+    )
+
+    ARG_OPTIONS=()
+
+    _handle_options || _project_completion
+}
+
+_repo_download() {
+    OPTIONS=(
+        "-h" "--help"
+    )
+
+    ARG_OPTIONS=()
+
+    _handle_options || _when_even _project_completion
+}
+
+_repo_forall() {
+    OPTIONS=(
+        "-h" "--help"
+        "-p"
+        "-v" "--verbose"
+    )
+
+    ARG_OPTIONS=(
+        ["-c"]=_path_cmd_completion
+        ["--command"]=_path_cmd_completion
+    )
+
+    _handle_options || _before _project_completion -c --command || _filedir
+}
+
+_repo_grep() {
+    OPTIONS=(
+        "-h" "--help"
+        "--cached"
+        "-r" "--revision"
+        "-i" "--ignore-case"
+        "-a" "--text"
+        "-I"
+        "-w" "--word-regexp"
+        "-v" "--invert-match"
+        "-G" "--basic-regexp"
+        "-E" "--extended-regexp"
+        "-F" "--fixed-strings"
+        "--all-match"
+        "--and" "--or" "--not"
+        "-(" "-)"
+        "-n"
+        "-l" "--name-only" "--files-with-matches"
+        "-L" "--files-without-match"
+    )
+
+    ARG_OPTIONS=(
+        ["-e"]=_no_completion
+        ["-C"]=_no_completion
+        ["-B"]=_no_completion
+        ["-A"]=_no_completion
+    )
+
+    _handle_options || _project_completion
+}
+
+_repo_list() {
+    OPTIONS=(
+        "-h" "--help"
+    )
+
+    ARG_OPTIONS=()
+
+    _handle_options || _project_completion
+}
+
+_repo_prune() {
+    OPTIONS=(
+        "-h" "--help"
+    )
+
+    ARG_OPTIONS=()
+
+    _handle_options || _project_completion
+}
+
+_repo_rebase() {
+    OPTIONS=(
+        "-h" "--help"
+        "-i" "--interactive"
+        "-f" "--force-rebase"
+        "--no-ff"
+        "-q" "--quiet"
+        "--autosquash"
+    )
+
+    ARG_OPTIONS=(
+        ["--whitespace"]=_no_completion
+    )
+
+    _handle_options || _project_completion
+}
+
+_repo_selfupdate() {
+    OPTIONS=(
+        "-h" "--help"
+        "--no-repo-verify"
+    )
+
+    ARG_OPTIONS=()
+
+    _handle_options
+}
+
+_repo_smartsync() {
+    OPTIONS=(
+        "-h" "--help"
+        "-f" "--force-broken"
+        "-l" "--local-only"
+        "-n" "--network-only"
+        "-d" "--detach"
+        "-q" "--quiet"
+        "--no-repo-verify"
+    )
+
+    ARG_OPTIONS=(
+        ["-j"]=_no_completion
+        ["--jobs"]=_no_completion
+
+    )
+
+    _handle_options || _project_completion
+}
+
+_repo_stage() {
+    OPTIONS=(
+        "-h" "--help"
+        "-i" "--interactive"
+    )
+
+    ARG_OPTIONS=()
+
+    _handle_options || _project_completion
+}
+
+_repo_start() {
+    OPTIONS=(
+        "-h" "--help"
+        "--all"
+    )
+
+    ARG_OPTIONS=()
+
+    _handle_options || _when_first _branch_completion || _project_completion
+}
+
+_repo_status() {
+    OPTIONS=(
+        "-h" "--help"
+    )
+
+    ARG_OPTIONS=(
+        ["-j"]=_no_completion
+        ["--jobs"]=_no_completion
+    )
+
+    _handle_options || _project_completion
+}
+
+_repo_sync() {
+    OPTIONS=(
+        "-h" "--help"
+        "-f" "--force-broken"
+             "--force-sync"
+        "-l" "--local-only"
+        "-n" "--network-only"
+        "-d" "--detach"
+        "-q" "--quiet"
+        "-s" "--smart-sync"
+        "--no-repo-verify"
+    )
+
+    ARG_OPTIONS=(
+        ["-j"]=_no_completion
+        ["--jobs"]=_no_completion
+    )
+
+    _handle_options || _project_completion
+}
+
+_repo_upload() {
+    OPTIONS=(
+        "-h" "--help"
+        "-t"
+        "--no-verify"
+        "--verify"
+    )
+
+    ARG_OPTIONS=(
+        ["--re"]=_no_completion
+        ["--reviewers"]=_no_completion
+        ["--cc"]=_no_completion
+        ["--br"]=_branch_completion
+    )
+
+    _handle_options || _project_completion
+}
+
+_repo_version() {
+    OPTIONS=(
+        "-h" "--help"
+    )
+
+    ARG_OPTIONS=()
+
+    _handle_options
+}
+
+_repo() {
+    COMPREPLY=()
+
+    _init_cur_prev
+
+    if [ ${COMP_CWORD} -eq 1 ]
+    then
+        _command_completion
+    else
+        local cmd=${COMP_WORDS[1]}
+        local handler=${CMD_HANDLERS["${cmd}"]}
+        if [ -n ${handler} ]
+        then
+            eval ${handler}
+        fi
+    fi
+
+    return 0
+}
+
+complete -o nospace -F _repo repo