g/goto.sh

143 lines
3.9 KiB
Bash

# * g (goto directory) - bookmark directories in Bash.
# * versuion: 0.2
# This is free and unencumbered software released into the public domain.
#
# Anyone is free to copy, modify, publish, use, compile, sell, or
# distribute this software, either in source code form or as a compiled
# binary, for any purpose, commercial or non-commercial, and by any
# means.
#
# In jurisdictions that recognize copyright laws, the author or authors
# of this software dedicate any and all copyright interest in the
# software to the public domain. We make this dedication for the benefit
# of the public at large and to the detriment of our heirs and
# successors. We intend this dedication to be an overt act of
# relinquishment in perpetuity of all present and future rights to this
# software under copyright law.
#
# 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 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.
#
# For more information, please refer to <http://unlicense.org/>
_goto_cd() {
##### cd into directory #####
if [ ! -d "$1" ]; then
echo "goto: no such directory $1" >&2
return 1
fi
if cd -- "$1" > /dev/null; then
echo "$1"
else
echo -e "\bgoto: cannot cd into $1" >&2
fi
return "$?"
}
_goto_prompt() {
##### Prompt and cd #####
# Exit if no dirs in ~/.goto_saved
if [ "${#dirs[@]}" -eq 0 ] || [[ "${dirs[@]}" == '' ]]; then
echo goto: nothing to do; return 1
fi
# cd into dir without prompt if you have single directory
# in ~/.goto_saved
if [ "${#dirs[@]}" -eq 1 ]; then
_goto_cd "${dirs[@]}" && return 0
fi
# Print directory list
for i in "${!dirs[@]}"; do
echo -e "$i\t${dirs[$i]%%+(/)}/" | sed "s%${HOME}%~%"
done
# Count digits in number of dirs for 'read'
local dirscount="$((${#dirs[@]}-1))"
local numlen="${#dirscount}"
# Read input to 'num' and set 'target' directory
read -r -n "$numlen" -p ': ' num
if [[ "$num" =~ [0-9]+ ]]; then
target="${dirs[$num]}";
# Print new line to prevent line concatenation
[ "${#num}" -eq "$numlen" ] && echo
elif [[ "$num" == '' ]]; then
return 1 # Just exit if you press Enter.
else
# Print new line and exit if printable chars passed.
# E.g. new line will be printed if you type 'q'.
echo; return 1
fi
# cd into directory
_goto_cd "$target"
}
_goto_search() {
grep -iP "$1" ~/.goto_saved
}
_goto() {
##### Main function #####
# Get directory list ('dirs' array)
dirs=()
if [ -f ~/.goto_saved ]; then
if [ "$1" ]; then
# Search dirs with Perl regex
_source="$(_goto_search "$1")"
else
# Load all directories
_source="$(<~/.goto_saved)"
fi
while read -r dir; do
dirs+=("$dir")
done <<< "$_source"
fi
_goto_prompt # prompt and cd
}
_goto_save() {
##### Save PWD or 1 to ~/.goto_saved file #####
local dir
if [ "$1" ]; then
dir="$1"
else
dir="$PWD"
fi
# Exit if directory is already in ~/.goto_saved
if grep "^${dir%%+(/)}\$" ~/.goto_saved > /dev/null; then
return 1
fi
# Save directory
if echo "${dir%%+(/)}" >> ~/.goto_saved; then
echo "$dir"
fi
}
_goto_complete() {
# Autocomplete directory by pattern
local pattern=${COMP_WORDS[COMP_CWORD]}
local IFS=$'\n'
COMPREPLY=($(compgen -W "$(printf "%s\n" "$(_goto_search "$pattern")")" -- ''))
}
complete -F _goto_complete g
alias g='_goto'
alias g-save='_goto_save'