From: Iain Patterson Date: Wed, 22 Apr 2009 15:00:52 +0000 (+0100) Subject: PS1 overhaul. X-Git-Url: http://git.iain.cx/?a=commitdiff_plain;h=9b2d4a12c4cbeea017e1abdd506bf9dcdc5b4910;p=profile.git PS1 overhaul. Include Git and Perforce prompt information but disable them by default. Allow toggling user@host prompt with prompt hide|show user. Allow toggling Git prompt with prompt hide|show git. Allow toggling Perforce prompt with prompt hide|show p4. --- diff --git a/.profile.d/git-completion.bashrc b/.profile.d/git-completion.bashrc new file mode 100644 index 0000000..430f3d9 --- /dev/null +++ b/.profile.d/git-completion.bashrc @@ -0,0 +1,2016 @@ +#!bash +# +# bash completion support for core Git. +# +# Copyright (C) 2006,2007 Shawn O. Pearce +# Conceptually based on gitcompletion (http://gitweb.hawaga.org.uk/). +# Distributed under the GNU General Public License, version 2.0. +# +# The contained completion routines provide support for completing: +# +# *) local and remote branch names +# *) local and remote tag names +# *) .git/remotes file names +# *) git 'subcommands' +# *) tree paths within 'ref:path/to/file' expressions +# *) common --long-options +# +# To use these routines: +# +# 1) Copy this file to somewhere (e.g. ~/.git-completion.sh). +# 2) Added the following line to your .bashrc: +# source ~/.git-completion.sh +# +# 3) You may want to make sure the git executable is available +# in your PATH before this script is sourced, as some caching +# is performed while the script loads. If git isn't found +# at source time then all lookups will be done on demand, +# which may be slightly slower. +# +# 4) Consider changing your PS1 to also show the current branch: +# PS1='[\u@\h \W$(__git_ps1 " (%s)")]\$ ' +# +# The argument to __git_ps1 will be displayed only if you +# are currently in a git repository. The %s token will be +# the name of the current branch. +# +# In addition, if you set GIT_PS1_SHOWDIRTYSTATE to a nonempty +# value, unstaged (*) and staged (+) changes will be shown next +# to the branch name. You can configure this per-repository +# with the bash.showDirtyState variable, which defaults to true +# once GIT_PS1_SHOWDIRTYSTATE is enabled. +# +# To submit patches: +# +# *) Read Documentation/SubmittingPatches +# *) Send all patches to the current maintainer: +# +# "Shawn O. Pearce" +# +# *) Always CC the Git mailing list: +# +# git@vger.kernel.org +# + +case "$COMP_WORDBREAKS" in +*:*) : great ;; +*) COMP_WORDBREAKS="$COMP_WORDBREAKS:" +esac + +# None of this will work if git isn't available. +__gitcheck() +{ + if [ -z "$GIT_EXISTS" ]; then + if [ -z "$(git version 2>/dev/null)" ]; then + GIT_EXISTS=no + else + GIT_EXISTS=yes + fi + fi + + [ "$GIT_EXISTS" = "yes" ] + return $? +} + +# __gitdir accepts 0 or 1 arguments (i.e., location) +# returns location of .git repo +__gitdir () +{ + if ! __gitcheck; then + echo "$1" + elif [ -z "${1-}" ]; then + if [ -n "${__git_dir-}" ]; then + echo "$__git_dir" + elif [ -d .git ]; then + echo .git + else + git rev-parse --git-dir 2>/dev/null + fi + elif [ -d "$1/.git" ]; then + echo "$1/.git" + else + echo "$1" + fi +} + +# __git_ps1 accepts 0 or 1 arguments (i.e., format string) +# returns text to add to bash PS1 prompt (includes branch name) +__git_ps1 () +{ + local g="$(__gitdir)" + if [ -n "$g" ]; then + local r + local b + if [ -d "$g/rebase-apply" ]; then + if [ -f "$g/rebase-apply/rebasing" ]; then + r="|REBASE" + elif [ -f "$g/rebase-apply/applying" ]; then + r="|AM" + else + r="|AM/REBASE" + fi + b="$(git symbolic-ref HEAD 2>/dev/null)" + elif [ -f "$g/rebase-merge/interactive" ]; then + r="|REBASE-i" + b="$(cat "$g/rebase-merge/head-name")" + elif [ -d "$g/rebase-merge" ]; then + r="|REBASE-m" + b="$(cat "$g/rebase-merge/head-name")" + elif [ -f "$g/MERGE_HEAD" ]; then + r="|MERGING" + b="$(git symbolic-ref HEAD 2>/dev/null)" + else + if [ -f "$g/BISECT_LOG" ]; then + r="|BISECTING" + fi + if ! b="$(git symbolic-ref HEAD 2>/dev/null)"; then + if ! b="$(git describe --exact-match HEAD 2>/dev/null)"; then + if [ -r "$g/HEAD" ]; then + b="$(cut -c1-7 "$g/HEAD")..." + fi + fi + fi + fi + + local w + local i + local c + + if [ "true" = "$(git rev-parse --is-inside-git-dir 2>/dev/null)" ]; then + if [ "true" = "$(git config --bool core.bare 2>/dev/null)" ]; then + c="BARE:" + else + b="GIT_DIR!" + fi + elif [ "true" = "$(git rev-parse --is-inside-work-tree 2>/dev/null)" ]; then + if [ -n "${GIT_PS1_SHOWDIRTYSTATE-}" ]; then + if [ "$(git config --bool bash.showDirtyState)" != "false" ]; then + git diff --no-ext-diff --ignore-submodules \ + --quiet --exit-code || w="*" + if git rev-parse --quiet --verify HEAD >/dev/null; then + git diff-index --cached --quiet \ + --ignore-submodules HEAD -- || i="+" + else + i="#" + fi + fi + fi + fi + + if [ -n "$b" ]; then + if [ -n "${1-}" ]; then + printf "$1" "$c${b##refs/heads/}$w$i$r" + else + printf " (%s)" "$c${b##refs/heads/}$w$i$r" + fi + fi + fi +} + +# __gitcomp_1 requires 2 arguments +__gitcomp_1 () +{ + local c IFS=' '$'\t'$'\n' + for c in $1; do + case "$c$2" in + --*=*) printf %s$'\n' "$c$2" ;; + *.) printf %s$'\n' "$c$2" ;; + *) printf %s$'\n' "$c$2 " ;; + esac + done +} + +# __gitcomp accepts 1, 2, 3, or 4 arguments +# generates completion reply with compgen +__gitcomp () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + if [ $# -gt 2 ]; then + cur="$3" + fi + case "$cur" in + --*=) + COMPREPLY=() + ;; + *) + local IFS=$'\n' + COMPREPLY=($(compgen -P "${2-}" \ + -W "$(__gitcomp_1 "${1-}" "${4-}")" \ + -- "$cur")) + ;; + esac +} + +# __git_heads accepts 0 or 1 arguments (to pass to __gitdir) +__git_heads () +{ + local cmd i is_hash=y dir="$(__gitdir "${1-}")" + if [ -d "$dir" ]; then + git --git-dir="$dir" for-each-ref --format='%(refname:short)' \ + refs/heads + return + fi + for i in $(git ls-remote "${1-}" 2>/dev/null); do + case "$is_hash,$i" in + y,*) is_hash=n ;; + n,*^{}) is_hash=y ;; + n,refs/heads/*) is_hash=y; echo "${i#refs/heads/}" ;; + n,*) is_hash=y; echo "$i" ;; + esac + done +} + +# __git_tags accepts 0 or 1 arguments (to pass to __gitdir) +__git_tags () +{ + local cmd i is_hash=y dir="$(__gitdir "${1-}")" + if [ -d "$dir" ]; then + git --git-dir="$dir" for-each-ref --format='%(refname:short)' \ + refs/tags + return + fi + for i in $(git ls-remote "${1-}" 2>/dev/null); do + case "$is_hash,$i" in + y,*) is_hash=n ;; + n,*^{}) is_hash=y ;; + n,refs/tags/*) is_hash=y; echo "${i#refs/tags/}" ;; + n,*) is_hash=y; echo "$i" ;; + esac + done +} + +# __git_refs accepts 0 or 1 arguments (to pass to __gitdir) +__git_refs () +{ + local i is_hash=y dir="$(__gitdir "${1-}")" + local cur="${COMP_WORDS[COMP_CWORD]}" format refs + if [ -d "$dir" ]; then + case "$cur" in + refs|refs/*) + format="refname" + refs="${cur%/*}" + ;; + *) + if [ -e "$dir/HEAD" ]; then echo HEAD; fi + format="refname:short" + refs="refs/tags refs/heads refs/remotes" + ;; + esac + git --git-dir="$dir" for-each-ref --format="%($format)" \ + $refs + return + fi + for i in $(git ls-remote "$dir" 2>/dev/null); do + case "$is_hash,$i" in + y,*) is_hash=n ;; + n,*^{}) is_hash=y ;; + n,refs/tags/*) is_hash=y; echo "${i#refs/tags/}" ;; + n,refs/heads/*) is_hash=y; echo "${i#refs/heads/}" ;; + n,refs/remotes/*) is_hash=y; echo "${i#refs/remotes/}" ;; + n,*) is_hash=y; echo "$i" ;; + esac + done +} + +# __git_refs2 requires 1 argument (to pass to __git_refs) +__git_refs2 () +{ + local i + for i in $(__git_refs "$1"); do + echo "$i:$i" + done +} + +# __git_refs_remotes requires 1 argument (to pass to ls-remote) +__git_refs_remotes () +{ + local cmd i is_hash=y + for i in $(git ls-remote "$1" 2>/dev/null); do + case "$is_hash,$i" in + n,refs/heads/*) + is_hash=y + echo "$i:refs/remotes/$1/${i#refs/heads/}" + ;; + y,*) is_hash=n ;; + n,*^{}) is_hash=y ;; + n,refs/tags/*) is_hash=y;; + n,*) is_hash=y; ;; + esac + done +} + +__git_remotes () +{ + local i ngoff IFS=$'\n' d="$(__gitdir)" + shopt -q nullglob || ngoff=1 + shopt -s nullglob + for i in "$d/remotes"/*; do + echo ${i#$d/remotes/} + done + [ "$ngoff" ] && shopt -u nullglob + for i in $(git --git-dir="$d" config --list); do + case "$i" in + remote.*.url=*) + i="${i#remote.}" + echo "${i/.url=*/}" + ;; + esac + done +} + +__git_merge_strategies () +{ + if [ -n "${__git_merge_strategylist-}" ]; then + echo "$__git_merge_strategylist" + return + fi + git merge -s help 2>&1 | + sed -n -e '/[Aa]vailable strategies are: /,/^$/{ + s/\.$// + s/.*:// + s/^[ ]*// + s/[ ]*$// + p + }' +} +__git_merge_strategylist= +__git_merge_strategylist=$(__git_merge_strategies 2>/dev/null) + +__git_complete_file () +{ + local pfx ls ref cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + ?*:*) + ref="${cur%%:*}" + cur="${cur#*:}" + case "$cur" in + ?*/*) + pfx="${cur%/*}" + cur="${cur##*/}" + ls="$ref:$pfx" + pfx="$pfx/" + ;; + *) + ls="$ref" + ;; + esac + + case "$COMP_WORDBREAKS" in + *:*) : great ;; + *) pfx="$ref:$pfx" ;; + esac + + local IFS=$'\n' + COMPREPLY=($(compgen -P "$pfx" \ + -W "$(git --git-dir="$(__gitdir)" ls-tree "$ls" \ + | sed '/^100... blob /{ + s,^.* ,, + s,$, , + } + /^120000 blob /{ + s,^.* ,, + s,$, , + } + /^040000 tree /{ + s,^.* ,, + s,$,/, + } + s/^.* //')" \ + -- "$cur")) + ;; + *) + __gitcomp "$(__git_refs)" + ;; + esac +} + +__git_complete_revlist () +{ + local pfx cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + *...*) + pfx="${cur%...*}..." + cur="${cur#*...}" + __gitcomp "$(__git_refs)" "$pfx" "$cur" + ;; + *..*) + pfx="${cur%..*}.." + cur="${cur#*..}" + __gitcomp "$(__git_refs)" "$pfx" "$cur" + ;; + *) + __gitcomp "$(__git_refs)" + ;; + esac +} + +__git_complete_remote_or_refspec () +{ + local cmd="${COMP_WORDS[1]}" + local cur="${COMP_WORDS[COMP_CWORD]}" + local i c=2 remote="" pfx="" lhs=1 no_complete_refspec=0 + while [ $c -lt $COMP_CWORD ]; do + i="${COMP_WORDS[c]}" + case "$i" in + --all|--mirror) [ "$cmd" = "push" ] && no_complete_refspec=1 ;; + -*) ;; + *) remote="$i"; break ;; + esac + c=$((++c)) + done + if [ -z "$remote" ]; then + __gitcomp "$(__git_remotes)" + return + fi + if [ $no_complete_refspec = 1 ]; then + COMPREPLY=() + return + fi + [ "$remote" = "." ] && remote= + case "$cur" in + *:*) + case "$COMP_WORDBREAKS" in + *:*) : great ;; + *) pfx="${cur%%:*}:" ;; + esac + cur="${cur#*:}" + lhs=0 + ;; + +*) + pfx="+" + cur="${cur#+}" + ;; + esac + case "$cmd" in + fetch) + if [ $lhs = 1 ]; then + __gitcomp "$(__git_refs2 "$remote")" "$pfx" "$cur" + else + __gitcomp "$(__git_refs)" "$pfx" "$cur" + fi + ;; + pull) + if [ $lhs = 1 ]; then + __gitcomp "$(__git_refs "$remote")" "$pfx" "$cur" + else + __gitcomp "$(__git_refs)" "$pfx" "$cur" + fi + ;; + push) + if [ $lhs = 1 ]; then + __gitcomp "$(__git_refs)" "$pfx" "$cur" + else + __gitcomp "$(__git_refs "$remote")" "$pfx" "$cur" + fi + ;; + esac +} + +__git_complete_strategy () +{ + case "${COMP_WORDS[COMP_CWORD-1]}" in + -s|--strategy) + __gitcomp "$(__git_merge_strategies)" + return 0 + esac + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --strategy=*) + __gitcomp "$(__git_merge_strategies)" "" "${cur##--strategy=}" + return 0 + ;; + esac + return 1 +} + +__git_all_commands () +{ + if [ -n "${__git_all_commandlist-}" ]; then + echo "$__git_all_commandlist" + return + fi + local i IFS=" "$'\n' + for i in $(git help -a|egrep '^ ') + do + case $i in + *--*) : helper pattern;; + *) echo $i;; + esac + done +} +__git_all_commandlist= +__git_all_commandlist="$(__git_all_commands 2>/dev/null)" + +__git_porcelain_commands () +{ + if [ -n "${__git_porcelain_commandlist-}" ]; then + echo "$__git_porcelain_commandlist" + return + fi + local i IFS=" "$'\n' + for i in "help" $(__git_all_commands) + do + case $i in + *--*) : helper pattern;; + applymbox) : ask gittus;; + applypatch) : ask gittus;; + archimport) : import;; + cat-file) : plumbing;; + check-attr) : plumbing;; + check-ref-format) : plumbing;; + checkout-index) : plumbing;; + commit-tree) : plumbing;; + count-objects) : infrequent;; + cvsexportcommit) : export;; + cvsimport) : import;; + cvsserver) : daemon;; + daemon) : daemon;; + diff-files) : plumbing;; + diff-index) : plumbing;; + diff-tree) : plumbing;; + fast-import) : import;; + fast-export) : export;; + fsck-objects) : plumbing;; + fetch-pack) : plumbing;; + fmt-merge-msg) : plumbing;; + for-each-ref) : plumbing;; + hash-object) : plumbing;; + http-*) : transport;; + index-pack) : plumbing;; + init-db) : deprecated;; + local-fetch) : plumbing;; + lost-found) : infrequent;; + ls-files) : plumbing;; + ls-remote) : plumbing;; + ls-tree) : plumbing;; + mailinfo) : plumbing;; + mailsplit) : plumbing;; + merge-*) : plumbing;; + mktree) : plumbing;; + mktag) : plumbing;; + pack-objects) : plumbing;; + pack-redundant) : plumbing;; + pack-refs) : plumbing;; + parse-remote) : plumbing;; + patch-id) : plumbing;; + peek-remote) : plumbing;; + prune) : plumbing;; + prune-packed) : plumbing;; + quiltimport) : import;; + read-tree) : plumbing;; + receive-pack) : plumbing;; + reflog) : plumbing;; + repo-config) : deprecated;; + rerere) : plumbing;; + rev-list) : plumbing;; + rev-parse) : plumbing;; + runstatus) : plumbing;; + sh-setup) : internal;; + shell) : daemon;; + show-ref) : plumbing;; + send-pack) : plumbing;; + show-index) : plumbing;; + ssh-*) : transport;; + stripspace) : plumbing;; + symbolic-ref) : plumbing;; + tar-tree) : deprecated;; + unpack-file) : plumbing;; + unpack-objects) : plumbing;; + update-index) : plumbing;; + update-ref) : plumbing;; + update-server-info) : daemon;; + upload-archive) : plumbing;; + upload-pack) : plumbing;; + write-tree) : plumbing;; + var) : infrequent;; + verify-pack) : infrequent;; + verify-tag) : plumbing;; + *) echo $i;; + esac + done +} +__git_porcelain_commandlist= +__git_porcelain_commandlist="$(__git_porcelain_commands 2>/dev/null)" + +__git_aliases () +{ + local i IFS=$'\n' + for i in $(git --git-dir="$(__gitdir)" config --list); do + case "$i" in + alias.*) + i="${i#alias.}" + echo "${i/=*/}" + ;; + esac + done +} + +# __git_aliased_command requires 1 argument +__git_aliased_command () +{ + local word cmdline=$(git --git-dir="$(__gitdir)" \ + config --get "alias.$1") + for word in $cmdline; do + if [ "${word##-*}" ]; then + echo $word + return + fi + done +} + +# __git_find_subcommand requires 1 argument +__git_find_subcommand () +{ + local word subcommand c=1 + + while [ $c -lt $COMP_CWORD ]; do + word="${COMP_WORDS[c]}" + for subcommand in $1; do + if [ "$subcommand" = "$word" ]; then + echo "$subcommand" + return + fi + done + c=$((++c)) + done +} + +__git_has_doubledash () +{ + local c=1 + while [ $c -lt $COMP_CWORD ]; do + if [ "--" = "${COMP_WORDS[c]}" ]; then + return 0 + fi + c=$((++c)) + done + return 1 +} + +__git_whitespacelist="nowarn warn error error-all fix" + +_git_am () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" dir="$(__gitdir)" + if [ -d "$dir"/rebase-apply ]; then + __gitcomp "--skip --resolved --abort" + return + fi + case "$cur" in + --whitespace=*) + __gitcomp "$__git_whitespacelist" "" "${cur##--whitespace=}" + return + ;; + --*) + __gitcomp " + --3way --committer-date-is-author-date --ignore-date + --interactive --keep --no-utf8 --signoff --utf8 + --whitespace= + " + return + esac + COMPREPLY=() +} + +_git_apply () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --whitespace=*) + __gitcomp "$__git_whitespacelist" "" "${cur##--whitespace=}" + return + ;; + --*) + __gitcomp " + --stat --numstat --summary --check --index + --cached --index-info --reverse --reject --unidiff-zero + --apply --no-add --exclude= + --whitespace= --inaccurate-eof --verbose + " + return + esac + COMPREPLY=() +} + +_git_add () +{ + __git_has_doubledash && return + + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp " + --interactive --refresh --patch --update --dry-run + --ignore-errors --intent-to-add + " + return + esac + COMPREPLY=() +} + +_git_archive () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --format=*) + __gitcomp "$(git archive --list)" "" "${cur##--format=}" + return + ;; + --remote=*) + __gitcomp "$(__git_remotes)" "" "${cur##--remote=}" + return + ;; + --*) + __gitcomp " + --format= --list --verbose + --prefix= --remote= --exec= + " + return + ;; + esac + __git_complete_file +} + +_git_bisect () +{ + __git_has_doubledash && return + + local subcommands="start bad good skip reset visualize replay log run" + local subcommand="$(__git_find_subcommand "$subcommands")" + if [ -z "$subcommand" ]; then + __gitcomp "$subcommands" + return + fi + + case "$subcommand" in + bad|good|reset|skip) + __gitcomp "$(__git_refs)" + ;; + *) + COMPREPLY=() + ;; + esac +} + +_git_branch () +{ + local i c=1 only_local_ref="n" has_r="n" + + while [ $c -lt $COMP_CWORD ]; do + i="${COMP_WORDS[c]}" + case "$i" in + -d|-m) only_local_ref="y" ;; + -r) has_r="y" ;; + esac + c=$((++c)) + done + + case "${COMP_WORDS[COMP_CWORD]}" in + --*) + __gitcomp " + --color --no-color --verbose --abbrev= --no-abbrev + --track --no-track --contains --merged --no-merged + " + ;; + *) + if [ $only_local_ref = "y" -a $has_r = "n" ]; then + __gitcomp "$(__git_heads)" + else + __gitcomp "$(__git_refs)" + fi + ;; + esac +} + +_git_bundle () +{ + local cmd="${COMP_WORDS[2]}" + case "$COMP_CWORD" in + 2) + __gitcomp "create list-heads verify unbundle" + ;; + 3) + # looking for a file + ;; + *) + case "$cmd" in + create) + __git_complete_revlist + ;; + esac + ;; + esac +} + +_git_checkout () +{ + __git_has_doubledash && return + + __gitcomp "$(__git_refs)" +} + +_git_cherry () +{ + __gitcomp "$(__git_refs)" +} + +_git_cherry_pick () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp "--edit --no-commit" + ;; + *) + __gitcomp "$(__git_refs)" + ;; + esac +} + +_git_clean () +{ + __git_has_doubledash && return + + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp "--dry-run --quiet" + return + ;; + esac + COMPREPLY=() +} + +_git_clone () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp " + --local + --no-hardlinks + --shared + --reference + --quiet + --no-checkout + --bare + --mirror + --origin + --upload-pack + --template= + --depth + " + return + ;; + esac + COMPREPLY=() +} + +_git_commit () +{ + __git_has_doubledash && return + + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp " + --all --author= --signoff --verify --no-verify + --edit --amend --include --only --interactive + " + return + esac + COMPREPLY=() +} + +_git_describe () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp " + --all --tags --contains --abbrev= --candidates= + --exact-match --debug --long --match --always + " + return + esac + __gitcomp "$(__git_refs)" +} + +__git_diff_common_options="--stat --numstat --shortstat --summary + --patch-with-stat --name-only --name-status --color + --no-color --color-words --no-renames --check + --full-index --binary --abbrev --diff-filter= + --find-copies-harder + --text --ignore-space-at-eol --ignore-space-change + --ignore-all-space --exit-code --quiet --ext-diff + --no-ext-diff + --no-prefix --src-prefix= --dst-prefix= + --inter-hunk-context= + --patience + --raw +" + +_git_diff () +{ + __git_has_doubledash && return + + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp "--cached --staged --pickaxe-all --pickaxe-regex + --base --ours --theirs + $__git_diff_common_options + " + return + ;; + esac + __git_complete_file +} + +__git_mergetools_common="diffuse ecmerge emerge kdiff3 meld opendiff + tkdiff vimdiff gvimdiff xxdiff +" + +_git_difftool () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --tool=*) + __gitcomp "$__git_mergetools_common kompare" "" "${cur##--tool=}" + return + ;; + --*) + __gitcomp "--tool=" + return + ;; + esac + COMPREPLY=() +} + +__git_fetch_options=" + --quiet --verbose --append --upload-pack --force --keep --depth= + --tags --no-tags +" + +_git_fetch () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp "$__git_fetch_options" + return + ;; + esac + __git_complete_remote_or_refspec +} + +_git_format_patch () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --thread=*) + __gitcomp " + deep shallow + " "" "${cur##--thread=}" + return + ;; + --*) + __gitcomp " + --stdout --attach --no-attach --thread --thread= + --output-directory + --numbered --start-number + --numbered-files + --keep-subject + --signoff + --in-reply-to= --cc= + --full-index --binary + --not --all + --cover-letter + --no-prefix --src-prefix= --dst-prefix= + --inline --suffix= --ignore-if-in-upstream + --subject-prefix= + " + return + ;; + esac + __git_complete_revlist +} + +_git_fsck () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp " + --tags --root --unreachable --cache --no-reflogs --full + --strict --verbose --lost-found + " + return + ;; + esac + COMPREPLY=() +} + +_git_gc () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp "--prune --aggressive" + return + ;; + esac + COMPREPLY=() +} + +_git_grep () +{ + __git_has_doubledash && return + + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp " + --cached + --text --ignore-case --word-regexp --invert-match + --full-name + --extended-regexp --basic-regexp --fixed-strings + --files-with-matches --name-only + --files-without-match + --count + --and --or --not --all-match + " + return + ;; + esac + COMPREPLY=() +} + +_git_help () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp "--all --info --man --web" + return + ;; + esac + __gitcomp "$(__git_all_commands) + attributes cli core-tutorial cvs-migration + diffcore gitk glossary hooks ignore modules + repository-layout tutorial tutorial-2 + workflows + " +} + +_git_init () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --shared=*) + __gitcomp " + false true umask group all world everybody + " "" "${cur##--shared=}" + return + ;; + --*) + __gitcomp "--quiet --bare --template= --shared --shared=" + return + ;; + esac + COMPREPLY=() +} + +_git_ls_files () +{ + __git_has_doubledash && return + + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp "--cached --deleted --modified --others --ignored + --stage --directory --no-empty-directory --unmerged + --killed --exclude= --exclude-from= + --exclude-per-directory= --exclude-standard + --error-unmatch --with-tree= --full-name + --abbrev --ignored --exclude-per-directory + " + return + ;; + esac + COMPREPLY=() +} + +_git_ls_remote () +{ + __gitcomp "$(__git_remotes)" +} + +_git_ls_tree () +{ + __git_complete_file +} + +# Options that go well for log, shortlog and gitk +__git_log_common_options=" + --not --all + --branches --tags --remotes + --first-parent --no-merges + --max-count= + --max-age= --since= --after= + --min-age= --until= --before= +" +# Options that go well for log and gitk (not shortlog) +__git_log_gitk_options=" + --dense --sparse --full-history + --simplify-merges --simplify-by-decoration + --left-right +" +# Options that go well for log and shortlog (not gitk) +__git_log_shortlog_options=" + --author= --committer= --grep= + --all-match +" + +__git_log_pretty_formats="oneline short medium full fuller email raw format:" + +_git_log () +{ + __git_has_doubledash && return + + local cur="${COMP_WORDS[COMP_CWORD]}" + local g="$(git rev-parse --git-dir 2>/dev/null)" + local merge="" + if [ -f "$g/MERGE_HEAD" ]; then + merge="--merge" + fi + case "$cur" in + --pretty=*) + __gitcomp "$__git_log_pretty_formats + " "" "${cur##--pretty=}" + return + ;; + --format=*) + __gitcomp "$__git_log_pretty_formats + " "" "${cur##--format=}" + return + ;; + --date=*) + __gitcomp " + relative iso8601 rfc2822 short local default + " "" "${cur##--date=}" + return + ;; + --*) + __gitcomp " + $__git_log_common_options + $__git_log_shortlog_options + $__git_log_gitk_options + --root --topo-order --date-order --reverse + --follow + --abbrev-commit --abbrev= + --relative-date --date= + --pretty= --format= --oneline + --cherry-pick + --graph + --decorate + --walk-reflogs + --parents --children + $merge + $__git_diff_common_options + --pickaxe-all --pickaxe-regex + " + return + ;; + esac + __git_complete_revlist +} + +__git_merge_options=" + --no-commit --no-stat --log --no-log --squash --strategy + --commit --stat --no-squash --ff --no-ff +" + +_git_merge () +{ + __git_complete_strategy && return + + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp "$__git_merge_options" + return + esac + __gitcomp "$(__git_refs)" +} + +_git_mergetool () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --tool=*) + __gitcomp "$__git_mergetools_common tortoisemerge" "" "${cur##--tool=}" + return + ;; + --*) + __gitcomp "--tool=" + return + ;; + esac + COMPREPLY=() +} + +_git_merge_base () +{ + __gitcomp "$(__git_refs)" +} + +_git_mv () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp "--dry-run" + return + ;; + esac + COMPREPLY=() +} + +_git_name_rev () +{ + __gitcomp "--tags --all --stdin" +} + +_git_pull () +{ + __git_complete_strategy && return + + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp " + --rebase --no-rebase + $__git_merge_options + $__git_fetch_options + " + return + ;; + esac + __git_complete_remote_or_refspec +} + +_git_push () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "${COMP_WORDS[COMP_CWORD-1]}" in + --repo) + __gitcomp "$(__git_remotes)" + return + esac + case "$cur" in + --repo=*) + __gitcomp "$(__git_remotes)" "" "${cur##--repo=}" + return + ;; + --*) + __gitcomp " + --all --mirror --tags --dry-run --force --verbose + --receive-pack= --repo= + " + return + ;; + esac + __git_complete_remote_or_refspec +} + +_git_rebase () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" dir="$(__gitdir)" + if [ -d "$dir"/rebase-apply ] || [ -d "$dir"/rebase-merge ]; then + __gitcomp "--continue --skip --abort" + return + fi + __git_complete_strategy && return + case "$cur" in + --*) + __gitcomp "--onto --merge --strategy --interactive" + return + esac + __gitcomp "$(__git_refs)" +} + +_git_send_email () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp "--annotate --bcc --cc --cc-cmd --chain-reply-to + --compose --dry-run --envelope-sender --from --identity + --in-reply-to --no-chain-reply-to --no-signed-off-by-cc + --no-suppress-from --no-thread --quiet + --signed-off-by-cc --smtp-pass --smtp-server + --smtp-server-port --smtp-ssl --smtp-user --subject + --suppress-cc --suppress-from --thread --to + --validate --no-validate" + return + ;; + esac + COMPREPLY=() +} + +_git_config () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + local prv="${COMP_WORDS[COMP_CWORD-1]}" + case "$prv" in + branch.*.remote) + __gitcomp "$(__git_remotes)" + return + ;; + branch.*.merge) + __gitcomp "$(__git_refs)" + return + ;; + remote.*.fetch) + local remote="${prv#remote.}" + remote="${remote%.fetch}" + __gitcomp "$(__git_refs_remotes "$remote")" + return + ;; + remote.*.push) + local remote="${prv#remote.}" + remote="${remote%.push}" + __gitcomp "$(git --git-dir="$(__gitdir)" \ + for-each-ref --format='%(refname):%(refname)' \ + refs/heads)" + return + ;; + pull.twohead|pull.octopus) + __gitcomp "$(__git_merge_strategies)" + return + ;; + color.branch|color.diff|color.interactive|color.status|color.ui) + __gitcomp "always never auto" + return + ;; + color.pager) + __gitcomp "false true" + return + ;; + color.*.*) + __gitcomp " + normal black red green yellow blue magenta cyan white + bold dim ul blink reverse + " + return + ;; + *.*) + COMPREPLY=() + return + ;; + esac + case "$cur" in + --*) + __gitcomp " + --global --system --file= + --list --replace-all + --get --get-all --get-regexp + --add --unset --unset-all + --remove-section --rename-section + " + return + ;; + branch.*.*) + local pfx="${cur%.*}." + cur="${cur##*.}" + __gitcomp "remote merge mergeoptions" "$pfx" "$cur" + return + ;; + branch.*) + local pfx="${cur%.*}." + cur="${cur#*.}" + __gitcomp "$(__git_heads)" "$pfx" "$cur" "." + return + ;; + remote.*.*) + local pfx="${cur%.*}." + cur="${cur##*.}" + __gitcomp " + url proxy fetch push mirror skipDefaultUpdate + receivepack uploadpack tagopt + " "$pfx" "$cur" + return + ;; + remote.*) + local pfx="${cur%.*}." + cur="${cur#*.}" + __gitcomp "$(__git_remotes)" "$pfx" "$cur" "." + return + ;; + esac + __gitcomp " + apply.whitespace + branch.autosetupmerge + branch.autosetuprebase + clean.requireForce + color.branch + color.branch.current + color.branch.local + color.branch.plain + color.branch.remote + color.diff + color.diff.commit + color.diff.frag + color.diff.meta + color.diff.new + color.diff.old + color.diff.plain + color.diff.whitespace + color.interactive + color.interactive.header + color.interactive.help + color.interactive.prompt + color.pager + color.status + color.status.added + color.status.changed + color.status.header + color.status.nobranch + color.status.untracked + color.status.updated + color.ui + commit.template + core.autocrlf + core.bare + core.compression + core.deltaBaseCacheLimit + core.editor + core.excludesfile + core.fileMode + core.fsyncobjectfiles + core.gitProxy + core.ignoreCygwinFSTricks + core.ignoreStat + core.logAllRefUpdates + core.loosecompression + core.packedGitLimit + core.packedGitWindowSize + core.pager + core.preferSymlinkRefs + core.preloadindex + core.quotepath + core.repositoryFormatVersion + core.safecrlf + core.sharedRepository + core.symlinks + core.trustctime + core.warnAmbiguousRefs + core.whitespace + core.worktree + diff.autorefreshindex + diff.external + diff.mnemonicprefix + diff.renameLimit + diff.renameLimit. + diff.renames + fetch.unpackLimit + format.headers + format.numbered + format.pretty + format.suffix + gc.aggressiveWindow + gc.auto + gc.autopacklimit + gc.packrefs + gc.pruneexpire + gc.reflogexpire + gc.reflogexpireunreachable + gc.rerereresolved + gc.rerereunresolved + gitcvs.allbinary + gitcvs.dbTableNamePrefix + gitcvs.dbdriver + gitcvs.dbname + gitcvs.dbpass + gitcvs.dbuser + gitcvs.enabled + gitcvs.logfile + gitcvs.usecrlfattr + gui.blamehistoryctx + gui.commitmsgwidth + gui.copyblamethreshold + gui.diffcontext + gui.encoding + gui.fastcopyblame + gui.matchtrackingbranch + gui.newbranchtemplate + gui.pruneduringfetch + gui.spellingdictionary + gui.trustmtime + help.autocorrect + help.browser + help.format + http.lowSpeedLimit + http.lowSpeedTime + http.maxRequests + http.noEPSV + http.proxy + http.sslCAInfo + http.sslCAPath + http.sslCert + http.sslKey + http.sslVerify + i18n.commitEncoding + i18n.logOutputEncoding + instaweb.browser + instaweb.httpd + instaweb.local + instaweb.modulepath + instaweb.port + log.date + log.showroot + man.viewer + merge.conflictstyle + merge.log + merge.renameLimit + merge.stat + merge.tool + merge.verbosity + mergetool.keepBackup + pack.compression + pack.deltaCacheLimit + pack.deltaCacheSize + pack.depth + pack.indexVersion + pack.packSizeLimit + pack.threads + pack.window + pack.windowMemory + pull.octopus + pull.twohead + receive.denyCurrentBranch + receive.denyDeletes + receive.denyNonFastForwards + receive.fsckObjects + receive.unpackLimit + repack.usedeltabaseoffset + rerere.autoupdate + rerere.enabled + showbranch.default + status.relativePaths + status.showUntrackedFiles + tar.umask + transfer.unpackLimit + user.email + user.name + user.signingkey + web.browser + branch. remote. + " +} + +_git_remote () +{ + local subcommands="add rename rm show prune update set-head" + local subcommand="$(__git_find_subcommand "$subcommands")" + if [ -z "$subcommand" ]; then + __gitcomp "$subcommands" + return + fi + + case "$subcommand" in + rename|rm|show|prune) + __gitcomp "$(__git_remotes)" + ;; + update) + local i c='' IFS=$'\n' + for i in $(git --git-dir="$(__gitdir)" config --list); do + case "$i" in + remotes.*) + i="${i#remotes.}" + c="$c ${i/=*/}" + ;; + esac + done + __gitcomp "$c" + ;; + *) + COMPREPLY=() + ;; + esac +} + +_git_reset () +{ + __git_has_doubledash && return + + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp "--merge --mixed --hard --soft" + return + ;; + esac + __gitcomp "$(__git_refs)" +} + +_git_revert () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp "--edit --mainline --no-edit --no-commit --signoff" + return + ;; + esac + __gitcomp "$(__git_refs)" +} + +_git_rm () +{ + __git_has_doubledash && return + + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp "--cached --dry-run --ignore-unmatch --quiet" + return + ;; + esac + COMPREPLY=() +} + +_git_shortlog () +{ + __git_has_doubledash && return + + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp " + $__git_log_common_options + $__git_log_shortlog_options + --numbered --summary + " + return + ;; + esac + __git_complete_revlist +} + +_git_show () +{ + __git_has_doubledash && return + + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --pretty=*) + __gitcomp "$__git_log_pretty_formats + " "" "${cur##--pretty=}" + return + ;; + --format=*) + __gitcomp "$__git_log_pretty_formats + " "" "${cur##--format=}" + return + ;; + --*) + __gitcomp "--pretty= --format= + $__git_diff_common_options + " + return + ;; + esac + __git_complete_file +} + +_git_show_branch () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp " + --all --remotes --topo-order --current --more= + --list --independent --merge-base --no-name + --sha1-name --topics --reflog + " + return + ;; + esac + __git_complete_revlist +} + +_git_stash () +{ + local subcommands='save list show apply clear drop pop create branch' + local subcommand="$(__git_find_subcommand "$subcommands")" + if [ -z "$subcommand" ]; then + __gitcomp "$subcommands" + else + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$subcommand,$cur" in + save,--*) + __gitcomp "--keep-index" + ;; + apply,--*) + __gitcomp "--index" + ;; + show,--*|drop,--*|pop,--*|branch,--*) + COMPREPLY=() + ;; + show,*|apply,*|drop,*|pop,*|branch,*) + __gitcomp "$(git --git-dir="$(__gitdir)" stash list \ + | sed -n -e 's/:.*//p')" + ;; + *) + COMPREPLY=() + ;; + esac + fi +} + +_git_submodule () +{ + __git_has_doubledash && return + + local subcommands="add status init update summary foreach sync" + if [ -z "$(__git_find_subcommand "$subcommands")" ]; then + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp "--quiet --cached" + ;; + *) + __gitcomp "$subcommands" + ;; + esac + return + fi +} + +_git_svn () +{ + local subcommands=" + init fetch clone rebase dcommit log find-rev + set-tree commit-diff info create-ignore propget + proplist show-ignore show-externals branch tag blame + migrate + " + local subcommand="$(__git_find_subcommand "$subcommands")" + if [ -z "$subcommand" ]; then + __gitcomp "$subcommands" + else + local remote_opts="--username= --config-dir= --no-auth-cache" + local fc_opts=" + --follow-parent --authors-file= --repack= + --no-metadata --use-svm-props --use-svnsync-props + --log-window-size= --no-checkout --quiet + --repack-flags --use-log-author --localtime + --ignore-paths= $remote_opts + " + local init_opts=" + --template= --shared= --trunk= --tags= + --branches= --stdlayout --minimize-url + --no-metadata --use-svm-props --use-svnsync-props + --rewrite-root= --prefix= --use-log-author + --add-author-from $remote_opts + " + local cmt_opts=" + --edit --rmdir --find-copies-harder --copy-similarity= + " + + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$subcommand,$cur" in + fetch,--*) + __gitcomp "--revision= --fetch-all $fc_opts" + ;; + clone,--*) + __gitcomp "--revision= $fc_opts $init_opts" + ;; + init,--*) + __gitcomp "$init_opts" + ;; + dcommit,--*) + __gitcomp " + --merge --strategy= --verbose --dry-run + --fetch-all --no-rebase --commit-url + --revision $cmt_opts $fc_opts + " + ;; + set-tree,--*) + __gitcomp "--stdin $cmt_opts $fc_opts" + ;; + create-ignore,--*|propget,--*|proplist,--*|show-ignore,--*|\ + show-externals,--*) + __gitcomp "--revision=" + ;; + log,--*) + __gitcomp " + --limit= --revision= --verbose --incremental + --oneline --show-commit --non-recursive + --authors-file= --color + " + ;; + rebase,--*) + __gitcomp " + --merge --verbose --strategy= --local + --fetch-all --dry-run $fc_opts + " + ;; + commit-diff,--*) + __gitcomp "--message= --file= --revision= $cmt_opts" + ;; + info,--*) + __gitcomp "--url" + ;; + branch,--*) + __gitcomp "--dry-run --message --tag" + ;; + tag,--*) + __gitcomp "--dry-run --message" + ;; + blame,--*) + __gitcomp "--git-format" + ;; + migrate,--*) + __gitcomp " + --config-dir= --ignore-paths= --minimize + --no-auth-cache --username= + " + ;; + *) + COMPREPLY=() + ;; + esac + fi +} + +_git_tag () +{ + local i c=1 f=0 + while [ $c -lt $COMP_CWORD ]; do + i="${COMP_WORDS[c]}" + case "$i" in + -d|-v) + __gitcomp "$(__git_tags)" + return + ;; + -f) + f=1 + ;; + esac + c=$((++c)) + done + + case "${COMP_WORDS[COMP_CWORD-1]}" in + -m|-F) + COMPREPLY=() + ;; + -*|tag) + if [ $f = 1 ]; then + __gitcomp "$(__git_tags)" + else + COMPREPLY=() + fi + ;; + *) + __gitcomp "$(__git_refs)" + ;; + esac +} + +_git () +{ + local i c=1 command __git_dir + + while [ $c -lt $COMP_CWORD ]; do + i="${COMP_WORDS[c]}" + case "$i" in + --git-dir=*) __git_dir="${i#--git-dir=}" ;; + --bare) __git_dir="." ;; + --version|-p|--paginate) ;; + --help) command="help"; break ;; + *) command="$i"; break ;; + esac + c=$((++c)) + done + + if [ -z "$command" ]; then + case "${COMP_WORDS[COMP_CWORD]}" in + --*) __gitcomp " + --paginate + --no-pager + --git-dir= + --bare + --version + --exec-path + --html-path + --work-tree= + --help + " + ;; + *) __gitcomp "$(__git_porcelain_commands) $(__git_aliases)" ;; + esac + return + fi + + local expansion=$(__git_aliased_command "$command") + [ "$expansion" ] && command="$expansion" + + case "$command" in + am) _git_am ;; + add) _git_add ;; + apply) _git_apply ;; + archive) _git_archive ;; + bisect) _git_bisect ;; + bundle) _git_bundle ;; + branch) _git_branch ;; + checkout) _git_checkout ;; + cherry) _git_cherry ;; + cherry-pick) _git_cherry_pick ;; + clean) _git_clean ;; + clone) _git_clone ;; + commit) _git_commit ;; + config) _git_config ;; + describe) _git_describe ;; + diff) _git_diff ;; + difftool) _git_difftool ;; + fetch) _git_fetch ;; + format-patch) _git_format_patch ;; + fsck) _git_fsck ;; + gc) _git_gc ;; + grep) _git_grep ;; + help) _git_help ;; + init) _git_init ;; + log) _git_log ;; + ls-files) _git_ls_files ;; + ls-remote) _git_ls_remote ;; + ls-tree) _git_ls_tree ;; + merge) _git_merge;; + mergetool) _git_mergetool;; + merge-base) _git_merge_base ;; + mv) _git_mv ;; + name-rev) _git_name_rev ;; + pull) _git_pull ;; + push) _git_push ;; + rebase) _git_rebase ;; + remote) _git_remote ;; + reset) _git_reset ;; + revert) _git_revert ;; + rm) _git_rm ;; + send-email) _git_send_email ;; + shortlog) _git_shortlog ;; + show) _git_show ;; + show-branch) _git_show_branch ;; + stash) _git_stash ;; + stage) _git_add ;; + submodule) _git_submodule ;; + svn) _git_svn ;; + tag) _git_tag ;; + whatchanged) _git_log ;; + *) COMPREPLY=() ;; + esac +} + +_gitk () +{ + __git_has_doubledash && return + + local cur="${COMP_WORDS[COMP_CWORD]}" + local g="$(__gitdir)" + local merge="" + if [ -f "$g/MERGE_HEAD" ]; then + merge="--merge" + fi + case "$cur" in + --*) + __gitcomp " + $__git_log_common_options + $__git_log_gitk_options + $merge + " + return + ;; + esac + __git_complete_revlist +} + +# Cache whether git exists. +__gitcheck + +if [ $BASH_VERSINFO -gt 2 ]; then +complete -o bashdefault -o default -o nospace -F _git git 2>/dev/null \ + || complete -o default -o nospace -F _git git +complete -o bashdefault -o default -o nospace -F _gitk gitk 2>/dev/null \ + || complete -o default -o nospace -F _gitk gitk +fi + +# The following are necessary only for Cygwin, and only are needed +# when the user has tab-completed the executable name and consequently +# included the '.exe' suffix. +# +if [ Cygwin = "$(uname -o 2>/dev/null)" ]; then +complete -o bashdefault -o default -o nospace -F _git git.exe 2>/dev/null \ + || complete -o default -o nospace -F _git git.exe +fi diff --git a/.profile.d/p4-completion.bashrc b/.profile.d/p4-completion.bashrc new file mode 100644 index 0000000..0800e23 --- /dev/null +++ b/.profile.d/p4-completion.bashrc @@ -0,0 +1,130 @@ +# vim: set syn=sh: +if [ $BASH_VERSINFO -gt 2 ]; then +if [ ! -z "$SUDO_USER" ]; then + __p4_cache_owner="$SUDO_USER" +else + __p4_cache_owner="$USER" +fi +__p4_cache="${TMPDIR:-/tmp}/.p4completion.$__p4_cache_owner" + +# Set up commands for completion. Assume the set of commands won't change +# within the session, as p4 help commands requires a network round trip. +function _p4() { + __p4_rebuild_cache + + local cmd=1 + while true; do + case ${COMP_WORDS[$cmd]} in + -G | -s | -??*) cmd=$((cmd+1));; + -?) cmd=$((cmd+2));; + *) break;; + esac + done + + if [ $COMP_CWORD = $cmd ] ; then + COMPREPLY=($(compgen -W "$__p4_all_commandlist" $2)) + elif [ $COMP_CWORD -gt $cmd ] ; then + COMPREPLY=($(compgen -f $2)) + fi +} +complete -F _p4 p4 + +# Check if we have p4. +function __p4check() { + if [ -z "$P4_EXISTS" ]; then + if [ -z "$(p4 -V 2>/dev/null)" ]; then + P4_EXISTS=no + else + P4_EXISTS=yes + fi + fi + + [ "$P4_EXISTS" = "yes" ] + return $? +} + +# Rebuild cache only if a p4 command was run or the cache is empty. +function __p4_dirty_cache() { + # Cache is dirty if it is empty. + [ -z "$__p4_all_commandlist" ] && return 0 + + # Cache is clean if no command was run. + [ "$__p4_histcmd" = "$hist" ] && return 1 + + # Cache is dirty if the last command was p4. + [ ! "${cmd/p4/}" = "$cmd" ] && return 0 + + # Cache is dirty if we changed directory. + [ ! "$PWD" = "$__p4_pwd" ] && return 0 + + # Cache is clean. + return 1 +} + +function __p4_rebuild_cache() { + __p4check || return + + __p4_load_cache + + local cmd=$(fc -l -1) + local hist=${cmd## *} + + if __p4_dirty_cache; then + __p4_all_commandlist="$(p4 help commands | awk 'NF > 3 { print $1 }')" + __p4_client + __p4_flags + fi + + __p4_histcmd="$hist" + __p4_pwd="$PWD" + + __p4_save_cache +} + +# Find the current client. +function __p4_client() { + local output="$(p4 -Ztag info)" + + __p4_client=$(echo "$output" | sed -n 's/^....clientName //p') + [ "$__p4_client" = "*unknown*" ] && __p4_client= + + __p4_pwd=$(echo "$output" | sed -n 's/^....clientCwd //p') +} + +# Look for opened and/or changed files. +function __p4_flags() { + __p4_flags= + + # Opened files? + if [ $(p4 opened 2>/dev/null | wc -l) -gt 0 ]; then + __p4_flags="$__p4_flags*" + fi + + # Changed files? + if [ $(p4 diff -sa -se 2>/dev/null | wc -l) -gt 0 ]; then + __p4_flags="$__p4_flags+" + fi +} + +function __p4_load_cache() { + [ -f "$__p4_cache" ] && . "$__p4_cache" +} + +function __p4_save_cache() { + touch "$__p4_cache" + chown "$__p4_cache_owner" "$__p4_cache" + chmod 600 "$__p4_cache" + set | grep "^__p4.*=" > "$__p4_cache" +} + +function __p4_ps1() { + __p4_rebuild_cache + if [ -n "$__p4_client" ]; then + if [ -n "${1-}" ]; then + printf "$1" "$__p4_client$__p4_flags" + else + printf " (%s)" "$__p4_client$__p4_flags" + fi + fi +} +fi diff --git a/.profile.d/ps1.bashrc b/.profile.d/ps1.bashrc index 1cffe4f..436cd8c 100644 --- a/.profile.d/ps1.bashrc +++ b/.profile.d/ps1.bashrc @@ -1,36 +1,72 @@ -# Coloured prompts for bash. +#!bash Coloured prompts. +# To use, add a call to __ps1 in your .bash_profile file. +# +# The prompt comprises multiple parts, some of which may be hidden by unsetting +# shell variables or using the ``prompt'' function. +# +# The first part of the prompt is user@host where host is highlighted in +# green if the last command exited 0 or in red otherwise. +# This part will be shown only if __ps1_user is 1. By default it is 1. +# The success and failure colours can be changed by modifying +# $PROMPT_OK_COLOUR and $PROMPT_FAILURE_COLOUR respectively. # -# Prompt is user@host:/dir$ where host is highlighted in green if the last -# command exited 0 or in red (followed by the error code) otherwise. +# The second part of the prompt is taken from git-completion.bashrc. +# It is shown only if __ps1_git is 1. By default it is 0. +# +# The third part of the prompt is taken from p4-completion.bashrc. +# It is shown only if __ps1_p4 is 1. By default it is 0. +# +# The fourth part of the prompt is the exit status of the last command. +# This part will be shown only if __ps1_user is set and the exit status is +# non-zero. +# +# The final part of the prompt is the (full) working directory and $ string. # # Colouring is performed by the __ps1_col() and __ps1_ret() functions. # We redirect stderr to /dev/null when calling these functions to prevent # bash complaining about not knowing them when you su to another user, # retaining PS1 but not the function definitions. # -# To use, add a call to __ps1 in your .bash_profile file. +# Note that $? is passed as an argument to - and is returned from - all +# functions. As $? is set following any shell activity it is only guaranteed +# to represent the return code of the last command at the beginning of __ps1(). +# By passing between subsequent functions we ensure that it is available for +# __ps1_ret(). # +# Pick a colour based on the terminal capabilities. +# OK: dark green. +# Failed: dark red. +# Git: royal blue. +# P4: yellow. case $(tput colors) in 256) export PROMPT_OK_COLOUR="1;38;5;34" export PROMPT_FAILED_COLOUR="1;38;5;160" + export GIT_COLOUR="0;38;5;33" + export P4_COLOUR="0;38;5;142" ;; 88) export PROMPT_OK_COLOUR="1;38;5;24" export PROMPT_FAILED_COLOUR="1;38;5;48" + export GIT_COLOUR="0;38;5;23" + export P4_COLOUR="0;38;5;56" ;; *) export PROMPT_OK_COLOUR="1;32" export PROMPT_FAILED_COLOUR="1;31" + export GIT_COLOUR="0;36" + export P4_COLOUR="0;33" ;; esac function __ps1() { - export PS1='\u@\[\033[$(__ps1_col $?)m\]\h\[\033[0m\]$(__ps1_ret $?):\w\$ ' - export PS1='\u@\[\033[$(__ps1_col $? 2>/dev/null)m\]\h\[\033[0m\]$(__ps1_ret $? 2>/dev/null):\w\$ ' + # Default __ps1_user to 1. + [ -z "$__ps1_user" ] && __ps1_user=1 + + export PS1='$(__ps1_user $? \u@)\[\033[$(__ps1_col $? 2>/dev/null)m\]$(__ps1_user $? \h)\[\033[${GIT_COLOUR}m\]$(__ps1_git $? 2>/dev/null)\[\033[0m\]\[\033[${P4_COLOUR}m\]$(__ps1_p4 $? 2>/dev/null)\[\033[0m\]$(__ps1_ret $? 2>/dev/null):\w\$ ' return 0 } @@ -40,6 +76,57 @@ function __ps1_col() { } function __ps1_ret() { + [ "$__ps1_user" = "1" ] || return $1 [ $1 -gt 0 ] && echo -n " ($1)" - return 0 + return $1 +} + +function __ps1_user() { + local ret=$1; shift + [ "$__ps1_user" = "1" ] || return $ret + echo -n ${1+"$@"} + return $ret +} + +function __ps1_git() { + [ "$__ps1_git" = "1" ] || return $1 + if [ "$__ps1_user" = "1" ]; then + __git_ps1 ' %s' + else + __git_ps1 '%s' + fi + return $1 +} + +function __ps1_p4() { + [ "$__ps1_p4" = "1" ] || return $1 + if [ "$__ps1_user" = "1" -o "$__ps1_git" = "1" ]; then + __p4_ps1 ' %s' + else + __p4_ps1 '%s' + fi + return $1 +} + +function prompt() { + local blurb="Usage: prompt hide|show " + + if [ $# -lt 2 ]; then + echo >&2 "$blurb" + return 1 + fi + + action="$1" + if [ ! "$action" = "hide" -a ! "$action" = "show" ]; then + echo >&2 "$blurb" + return 1 + fi + if [ "$action" = "hide" ]; then + action=0 + else + action=1 + fi + + what="$(echo $2 | env LANG= LC_ALL= LC_CTYPE= tr '[:upper:]' '[:lower:]')" + eval __ps1_$what=$action }