diff --git a/bash_completion/repo-LICENSE b/bash_completion/repo-LICENSE deleted file mode 100644 index d2514965..00000000 --- a/bash_completion/repo-LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/bash_completion/repo.bash b/bash_completion/repo.bash index e556d33a..3b1d510d 100644 --- a/bash_completion/repo.bash +++ b/bash_completion/repo.bash @@ -1,99 +1,654 @@ -# Copyright (c) 2011 The Chromium OS Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the repo-LICENSE file. +# -*- mode: sh; -*- -# Programmable completion for some Chromium OS build scripts. +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 +) -_list_repo_commands() { - local repo=${COMP_WORDS[0]} - "${repo}" help --all | grep -E '^ ' | sed 's/ \([^ ]\+\) .\+/\1/' +# 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 } -_list_repo_branches() { - local repo=${COMP_WORDS[0]} - "${repo}" branches 2>&1 | grep \| | sed 's/[ *][Pp ] *\([^ ]\+\) .*/\1/' -} +_find_repo() { + local dir=$(pwd) + local found=1 -_list_repo_projects() { - local repo=${COMP_WORDS[0]} - local manifest=$(mktemp) - "${repo}" manifest -o "${manifest}" >& /dev/null - grep 'project name=' "${manifest}" | sed 's/.\+name="\([^"]\+\)".\+/\1/' - rm -f "${manifest}" >& /dev/null -} + while [ "${dir}" != / ] + do + if [ -f "${dir}/.repo/repo/main.py" ] + then + found=0 + break + fi -# Complete the repo argument. -_complete_repo_command() { - [ ${COMP_CWORD} -eq 1 ] || return 1 - local command=${COMP_WORDS[1]} - COMPREPLY=($(compgen -W "$(_list_repo_commands)" -- "${command}")) - return 0 -} + dir=$(cd "${dir}/.." && pwd) + done -_complete_repo_arg() { - [ ${COMP_CWORD} -gt 1 ] || return 1 - local command=${COMP_WORDS[1]} - local current=${COMP_WORDS[COMP_CWORD]} - if [[ ${command} == "abandon" ]]; then - if [[ ${COMP_CWORD} -eq 2 ]]; then - COMPREPLY=($(compgen -W "$(_list_repo_branches)" -- "${current}")) - else - COMPREPLY=($(compgen -W "$(_list_repo_projects)" -- "${current}")) + 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 - fi - if [[ ${command} == "help" ]]; then - [ ${COMP_CWORD} -eq 2 ] && \ - COMPREPLY=($(compgen -W "$(_list_repo_commands)" -- "${current}")) +} + +_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 - fi - if [[ ${command} == "start" ]]; then - [ ${COMP_CWORD} -gt 2 ] && \ - COMPREPLY=($(compgen -W "$(_list_repo_projects)" -- "${current}")) +} + +_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" + "-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 - fi - return 1 } -# Complete the repo arguments. -_complete_repo() { - COMPREPLY=() - _complete_repo_command && return 0 - _complete_repo_arg && return 0 - return 0 -} - -complete -F _complete_repo repo - -# Add a way to get the "m" branch from repo easily; used by __git_branch_ps1() -# -# Repo seems to maintain a phony 'm/' remote and it always seems to be the name -# of the manifest branch. This will retrieve it. -__git_m_branch() { - local git_dir=$(git rev-parse --git-dir 2> /dev/null) - if [ -n "${git_dir}" ]; then - echo $(cd ${git_dir}/refs/remotes/m 2> /dev/null && ls) - fi -} - -# A "subclass" of __git_ps1 that adds the manifest branch name into the prompt. -# ...if you're on manifest branch "0.11.257.B" and local branch "lo" and -# pass " (%s)", we'll output " (0.11.257.B/lo)". Note that we'll never show -# the manifest branch 'master', since it's so common. -__git_branch_ps1() { - local format_str="${1:- (%s)}" - local m_branch=$(__git_m_branch) - if [ "${m_branch}" != "master" -a -n "${m_branch}" ]; then - format_str=$(printf "${format_str}" "${m_branch}/%s") - fi - # for subshells, prefix the prompt with the shell nesting level - local lshlvl="" - [ ! -z "${SHLVL##*[!0-9]*}" ] && [ ${SHLVL} -gt 1 ] && lshlvl="${SHLVL} " - __git_ps1 "${lshlvl}${format_str}" -} - -# Prompt functions should not error when in subshells -export -f __gitdir -export -f __git_ps1 -export -f __git_m_branch -export -f __git_branch_ps1 +complete -o nospace -F _repo repo