From b1747d35ec19df15c0c45273ecf02f3feb705b8c Mon Sep 17 00:00:00 2001 From: James Short Date: Thu, 8 Mar 2018 11:49:26 -0800 Subject: [PATCH] Add support for zsh completion. I also made the documentation and name of file more generic as it works for both zsh and bash. I removed the shebang since this file is simply sourced by the calling shell and it doesn't have executable permissions anyway. The various functions were updated to return non-zero on errors in case anyone uses goto in other scripts. The logic for parsing ~/.goto to determine which aliases can be cleaned was simplified into an awk script that removed the array manipulation logic that was not zsh compatible. --- README.md | 13 +++--- goto.bash => goto.sh | 100 ++++++++++++++++++++++++++++--------------- 2 files changed, 71 insertions(+), 42 deletions(-) rename goto.bash => goto.sh (79%) diff --git a/README.md b/README.md index b5c8737..9f35cb3 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # goto -`goto` is a bash utility allowing users to change faster to aliased directories supporting auto-completion :feet: +`goto` is a shell utility allowing users to change faster to aliased directories supporting auto-completion :feet: ## How does it work? @@ -17,7 +17,7 @@ goto dev ## goto completion -`goto` comes with a nice auto-completion script so that whenever you press the `tab` key after the `goto` command, bash prompts with suggestions of the available aliases: +`goto` comes with a nice auto-completion script so that whenever you press the `tab` key after the `goto` command, bash or zsh prompts with suggestions of the available aliases: ```bash $ goto @@ -28,12 +28,12 @@ rubies /home/iridakos/.rvm/rubies ## Installation -Copy the file `goto.bash` somewhere in your filesystem and add a line in your `.bashrc` to source it. +Copy the file `goto.sh` somewhere in your filesystem and add a line in your `.zshrc` or `.bashrc` to source it. -For example, if you placed the file in your home folder, all you have to do is add the following line to your `.bashrc` file: +For example, if you placed the file in your home folder, all you have to do is add the following line to your `.zshrc` or `.bashrc` file: ```bash -source ~/goto.bash +source ~/goto.sh ``` ## Usage @@ -75,7 +75,7 @@ goto --register blog /mnt/external/projects/html/blog goto -r last_release . ``` and it will automatically be aliased to the whole path. -* Pressing the `tab` key after the alias name, you have the default directory suggestions by bash. +* Pressing the `tab` key after the alias name, you have the default directory suggestions by the shell. ### Unregister an alias @@ -148,7 +148,6 @@ goto --version ## TODO * ~~Test on macOS~~ extensively -* Fix `zsh` issues [[#7](https://github.com/iridakos/goto/issues/7), ...] * Write [tests](https://github.com/iridakos/goto/issues/2) ## Contributing diff --git a/goto.bash b/goto.sh similarity index 79% rename from goto.bash rename to goto.sh index 2a5c4ba..5ad141c 100644 --- a/goto.bash +++ b/goto.sh @@ -1,4 +1,3 @@ -#!/usr/bin/env bash # MIT License # # Copyright (c) 2018 Lazarus Lazaridis @@ -58,6 +57,7 @@ function goto() _goto_directory "$subcommand" ;; esac + return $? } function _goto_usage() @@ -113,12 +113,12 @@ function _goto_register_alias() { if [ "$#" -ne "2" ]; then _goto_error "usage: goto -r|--register " - return + return 1 fi if ! [[ $1 =~ ^[[:alnum:]]+[a-zA-Z0-9_-]*$ ]]; then _goto_error "invalid alias - can start with letters or digits followed by letters, digits, hyphens or underscores" - return + return 1 fi local resolved @@ -126,14 +126,14 @@ function _goto_register_alias() if [ -n "$resolved" ]; then _goto_error "alias '$1' exists" - return + return 1 fi local directory directory=$(_goto_expand_directory "$2") if [ -z "$directory" ]; then _goto_error "failed to register '$1' to '$2' - can't cd to directory" - return + return 1 fi # Append entry to file. @@ -146,14 +146,14 @@ function _goto_unregister_alias { if [ "$#" -ne "1" ]; then _goto_error "usage: goto -u|--unregister " - return + return 1 fi local resolved resolved=$(_goto_find_alias_directory "$1") if [ -z "$resolved" ]; then _goto_error "alias '$1' does not exist" - return + return 1 fi # Delete entry from file. @@ -164,26 +164,15 @@ function _goto_unregister_alias # Unregisters aliases whose directories no longer exist. function _goto_cleanup() { - if ! [ -f ~/.goto ]; then + if [ ! -f ~/.goto ]; then return fi - local IFS=$'\n' match matches al dir - - read -d '' -r -a matches < ~/.goto - - IFS=' ' - for i in "${!matches[@]}"; do - read -r -a match <<< "${matches[$i]}" - - al="${match[0]}" - dir="${match[*]:1}" - - if [ -n "$al" ] && [ ! -d "$dir" ]; then - echo "Cleaning up: $al - $dir" - _goto_unregister_alias "$al" - fi - done + while IFS= read -r i && [ -n "$i" ]; do + echo "Cleaning up: $i" + _goto_unregister_alias "$i" + done <<< "$(awk '{al=$1; $1=""; dir=substr($0,2); + system("[ ! -d \"" dir "\" ] && echo " al)}' ~/.goto)" } # Changes to the given alias' directory @@ -191,11 +180,10 @@ function _goto_directory() { local target - target=$(_goto_resolve_alias "$1") + target=$(_goto_resolve_alias "$1") || return 1 - if [ -n "$target" ]; then - cd "$target" || _goto_error "Failed to goto '$target'" - fi + cd "$target" 2> /dev/null || \ + { _goto_error "Failed to goto '$target'" && return 1; } } # Fetches the alias directory. @@ -223,7 +211,7 @@ function _goto_resolve_alias() if [ -z "$resolved" ]; then _goto_error "unregistered alias $1" - echo "" + return 1 else echo "${resolved}" fi @@ -241,7 +229,7 @@ function _complete_goto_commands() # Completes the goto function with the available aliases function _complete_goto_aliases() { - local IFS=$'\n' matches al + local IFS=$'\n' matches # shellcheck disable=SC2207 matches=($(sed -n "/^$1/p" ~/.goto 2>/dev/null)) @@ -269,7 +257,7 @@ function _complete_goto_aliases() } # Bash programmable completion for the goto function -function _complete_goto() +function _complete_goto_bash() { local cur="${COMP_WORDS[$COMP_CWORD]}" prev @@ -304,9 +292,51 @@ function _complete_goto() fi } -# Register the goto compspec -if ! [[ $(uname -s) =~ Darwin* ]]; then - complete -o filenames -F _complete_goto goto +# Zsh programmable completion for the goto function +function _complete_goto_zsh() +{ + local all_aliases=() + while IFS= read -r line; do + all_aliases+=("$line") + done <<< "$(sed -e 's/ /:/g' ~/.goto 2>/dev/null)" + + local state + local -a options=( + '(1)'{-r,--register}'[registers an alias]:register:->register' + '(- 1 2)'{-u,--unregister}'[unregisters an alias]:unregister:->unregister' + '(: -)'{-l,--list}'[lists aliases]' + '(*)'{-c,--cleanup}'[cleans up non existent directory aliases]' + '(: -)'{-h,--help}'[prints this help]' + '(* -)'{-v,--version}'[displays the version of the goto script]' + ) + + _arguments -C \ + "${options[@]}" \ + '1:alias:->aliases' \ + '2:dir:_files' \ + && ret=0 + + case ${state} in + (aliases) + _describe -t aliases 'goto aliases:' all_aliases && ret=0 + ;; + (unregister) + _describe -t aliases 'unregister alias:' all_aliases && ret=0 + ;; + esac + return $ret +} + +# Register the goto completions. +if [ -n "${BASH_VERSION}" ]; then + if ! [[ $(uname -s) =~ Darwin* ]]; then + complete -o filenames -F _complete_goto_bash goto + else + complete -F _complete_goto_bash goto + fi +elif [ -n "${ZSH_VERSION}" ]; then + compdef _complete_goto_zsh goto else - complete -F _complete_goto goto + echo "Unsupported shell." + exit 1 fi