#!bash # ############################################################################### # # Bash completion for homeshick (https://github.com/andsens/homeshick). # # To use, add this line (or equivalent) to your .bashrc: # # source ~/.homesick/repos/homeshick/completions/homeshick-completion.bash # ############################################################################### # # Copyright (C) 2013 Jeremy Lin # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. # ############################################################################### _homeshick_basename() { echo "${1##*/}" } _homeshick_castles() { local repos="$HOME/.homesick/repos" # This should be a while loop like in link.sh, leave it for now though # shellcheck disable=SC2044 for repo in $(find -L "$repos" -mindepth 2 -maxdepth 2 -type d -name .git); do _homeshick_basename "${repo%/.git}" done } _homeshick_complete_castles() { COMPREPLY=($(compgen -W "$(_homeshick_castles)" -- "$1")) } _homeshick_complete() { # The comments at the bottom of the file explain what's going on here. if $_HOMESHICK_HAS_COMPOPT; then compopt +o default +o nospace COMPREPLY=() else COMPREPLY=('') fi local -r cmds=' cd clone generate list check refresh pull link track help symlink updates ' local -r short_opts='-q -s -f -b -v' local -r long_opts='--quiet --skip --force --batch --verbose' local -r protocols='file ftp ftps git http https rsync ssh' # Scan through the command line and find the homeshick command # (if present), as well as its expected position. local cmd local cmd_index=1 # Expected index of the command token. local i for (( i = 1; i < ${#COMP_WORDS[@]}; i++ )); do local word="${COMP_WORDS[i]}" case "$word" in -*) ((cmd_index++)) ;; *) cmd="$word" break ;; esac done local cur="${COMP_WORDS[COMP_CWORD]}" if (( COMP_CWORD < cmd_index )); then # Offer option completions. case "$cur" in --*) COMPREPLY=($(compgen -W "$long_opts" -- "$cur")) ;; -*) COMPREPLY=($(compgen -W "$short_opts" -- "$cur")) ;; *) # Skip completion; we should never get here. ;; esac elif (( COMP_CWORD == cmd_index )); then # Offer command name completions. COMPREPLY=($(compgen -W "$cmds" -- "$cur")) else # Offer command argument completions. case "$cmd" in check | pull | link | symlink | updates) # Offer one or more castle name completions. _homeshick_complete_castles "$cur" ;; cd) # Offer exactly one castle name completion. if (( COMP_CWORD == cmd_index + 1 )); then _homeshick_complete_castles "$cur" fi ;; refresh) # Offer a numerical completion for DAYS (mostly as a reminder # that this argument should be a number), then castle name # completions after that. if (( COMP_CWORD == cmd_index + 1 )); then COMPREPLY=({0..9}) else _homeshick_complete_castles "$cur" fi ;; track) # Offer one castle name completion, then filename completions # after that. if (( COMP_CWORD == cmd_index + 1 )); then _homeshick_complete_castles "$cur" else $_HOMESHICK_HAS_COMPOPT && compopt -o default # Let the default Readline filename completion take over. COMPREPLY=() fi ;; clone) # Offer an initial protocol completion. if (( COMP_CWORD == cmd_index + 1 )); then $_HOMESHICK_HAS_COMPOPT && compopt -o nospace COMPREPLY=($(compgen -W "$protocols" -S '://' -- "$cur")) fi ;; help) # Offer exactly one command name completion. if (( COMP_CWORD == cmd_index + 1 )); then COMPREPLY=($(compgen -W "$cmds" -- "$cur")) fi ;; *) # Unknown command or unknowable argument. ;; esac fi } # The behavior of 'compgen -f' is pretty bizarre, in that if there's a # directory 'foo', then compgen simply completes 'foo' and moves on, rather # than offering to complete names of files underneath 'foo', which is what # Bash (or Readline, actually) usually does. # # You can get the usual Readline filename completion behavior by doing # 'complete -o default -F ', in which case the Readline filename # completer is called if COMPREPLY=(). But this also means that you can no # longer deny completion with COMPREPLY=() -- now Readline will always offer # to complete a filename, even if this is inappropriate (e.g., the command # takes no further arguments). # # In Bash 4.0 and above, there's a way to work around this problem. These # versions have a 'compopt' builtin, which allows '-o default' to be enabled # or disabled as needed. The workaround, then, is to disable '-o default' # at the top of the completion function, and enable it as needed later on. # # For older Bash releases, there's a different workaround that doesn't work # quite as well, but is still better than nothing. This workaround is to # always enable '-o default' (which can't be disabled later), and then to use # COMPREPLY=('') to semi-deny completion. That is, pressing Tab will just add # space characters to the command line, rather than generating filenames. # if type compopt &>/dev/null; then _HOMESHICK_HAS_COMPOPT=true else _HOMESHICK_HAS_COMPOPT=false fi complete -o default -F _homeshick_complete homeshick