first commit
This commit is contained in:
44
completion.bash
Normal file
44
completion.bash
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
# goto completion -*- shell-script -*-
|
||||||
|
|
||||||
|
_comp_cmd_goto() {
|
||||||
|
local cur prev words cword comp_args
|
||||||
|
_comp_initialize -- "$@" || return
|
||||||
|
|
||||||
|
# echo "[$cur] [$prev] [${words[@]}] [$cword] [$comp_args]"
|
||||||
|
|
||||||
|
local -A opts=([-e]="" [-l]="" [-t]="")
|
||||||
|
|
||||||
|
local i
|
||||||
|
for i in "${!words[@]}"; do
|
||||||
|
[[ ${words[i]} && $i -ne $cword ]] && unset -v "opts[${words[i]}]"
|
||||||
|
case "${words[i]}" in
|
||||||
|
-e)
|
||||||
|
unset -v 'opts[-l]' 'opts[-t]'
|
||||||
|
;;
|
||||||
|
-l)
|
||||||
|
unset -v 'opts[-e]' 'opts[-t]'
|
||||||
|
;;
|
||||||
|
-t)
|
||||||
|
unset -v 'opts[-e]' 'opts[-l]'
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ $cur == -* ]]; then
|
||||||
|
_comp_compgen -- -W '"${!opts[@]}"'
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
case $prev in
|
||||||
|
-e)
|
||||||
|
[[ -v 'opts[-e]' ]] && __goto_comp_keys
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# do goto keys only if we did not have -[elt]
|
||||||
|
[[ ${words[*]} == *\ -* ]] || __goto_comp_keys
|
||||||
|
} &&
|
||||||
|
complete -F _comp_cmd_goto goto
|
||||||
|
|
||||||
|
# ex: filetype=sh
|
||||||
235
goto.bash
Normal file
235
goto.bash
Normal file
@@ -0,0 +1,235 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# the goto command allows changing directory using directory aliases
|
||||||
|
|
||||||
|
# directory where goto files live
|
||||||
|
readonly __GOTO_ROOT_DIR=~/.goto
|
||||||
|
# goto configuration file that contains the directory mappings
|
||||||
|
readonly __GOTO_FILE=${__GOTO_ROOT_DIR}/goto.yaml
|
||||||
|
# path to yq executable
|
||||||
|
readonly __GOTO_YQ=/usr/local/bin/yq
|
||||||
|
|
||||||
|
__goto_load() {
|
||||||
|
# load the directory mappings from the configuration file into the variable reference passed as parameter
|
||||||
|
# $1: name of the result associative array to create
|
||||||
|
|
||||||
|
local -n data_ref=$1
|
||||||
|
local entries entry key value
|
||||||
|
|
||||||
|
mapfile entries < <(${__GOTO_YQ} 'explode(.) | to_entries[] | "\(.key) \(.value)"' ${__GOTO_FILE})
|
||||||
|
|
||||||
|
data_ref=()
|
||||||
|
|
||||||
|
for entry in "${entries[@]}"; do
|
||||||
|
if [[ -z "${entry}" ]]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
IFS=' ' read key value <<< "${entry}"
|
||||||
|
|
||||||
|
if [[ -z "${key}" ]] || [[ -z "${value}" ]]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
data_ref["${key}"]="${value}"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
__goto_comp_keys() {
|
||||||
|
# loads and initializes directory aliases completions for the goto command
|
||||||
|
|
||||||
|
local -A data
|
||||||
|
__goto_load data
|
||||||
|
|
||||||
|
_comp_compgen -- -W '"${!data[@]}"'
|
||||||
|
}
|
||||||
|
|
||||||
|
__goto_resolve_path() {
|
||||||
|
# resolves a path to its canonical form
|
||||||
|
# $1: path to resolve
|
||||||
|
|
||||||
|
readlink -m "${1/#\~/${HOME}}"
|
||||||
|
}
|
||||||
|
|
||||||
|
__goto_prompt() {
|
||||||
|
# prompt string for the terminal
|
||||||
|
|
||||||
|
local -A data
|
||||||
|
__goto_load data
|
||||||
|
|
||||||
|
local entry_key entry_value best_match="" best_key=""
|
||||||
|
|
||||||
|
for entry_key in "${!data[@]}"; do
|
||||||
|
entry_value=$(__goto_resolve_path "${data[${entry_key}]}")
|
||||||
|
|
||||||
|
# select the closest ancestor of the current working directory
|
||||||
|
if [[ "${PWD}" == "${entry_value}"* ]] && [[ ${#entry_value} -gt ${#best_match} ]]; then
|
||||||
|
best_match="${entry_value}"
|
||||||
|
best_key="${entry_key}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ -n "${best_key}" ]]; then
|
||||||
|
local remaining="${PWD#${best_match}}"
|
||||||
|
printf '%s' "#${best_key}${remaining}"
|
||||||
|
else
|
||||||
|
local w='\w'
|
||||||
|
printf '%s' "${w@P}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
__goto_test() {
|
||||||
|
# validates the goto configuration file
|
||||||
|
# command: goto -t
|
||||||
|
|
||||||
|
local -A data
|
||||||
|
__goto_load data
|
||||||
|
|
||||||
|
local entry_key entry_value
|
||||||
|
local -i ret_code=0
|
||||||
|
|
||||||
|
for entry_key in "${!data[@]}"; do
|
||||||
|
entry_value=$(__goto_resolve_path "${data[${entry_key}]}")
|
||||||
|
|
||||||
|
if ! [[ -e "${entry_value}" ]]; then
|
||||||
|
echo "Not found: ${entry_key} → ${entry_value}"
|
||||||
|
ret_code=1
|
||||||
|
elif ! [[ -d "${entry_value}" ]]; then
|
||||||
|
echo "Not a directory: ${entry_key} → ${entry_value}"
|
||||||
|
ret_code=1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if (( ret_code == 0 )); then
|
||||||
|
echo 'Configuration ok'
|
||||||
|
fi
|
||||||
|
|
||||||
|
return ${ret_code}
|
||||||
|
}
|
||||||
|
|
||||||
|
__goto_edit() {
|
||||||
|
# edits the goto configuration file
|
||||||
|
# command: goto -e
|
||||||
|
|
||||||
|
nano ${__GOTO_FILE}
|
||||||
|
}
|
||||||
|
|
||||||
|
__goto_list() {
|
||||||
|
# displays the goto configuration file
|
||||||
|
# command: goto -l
|
||||||
|
|
||||||
|
local -A data
|
||||||
|
__goto_load data
|
||||||
|
|
||||||
|
local -a sorted_keys
|
||||||
|
mapfile -d '' sorted_keys < <(printf '%s\0' "${!data[@]}" | sort -z)
|
||||||
|
|
||||||
|
local entry_key entry_value
|
||||||
|
|
||||||
|
for entry_key in "${sorted_keys[@]}"; do
|
||||||
|
entry_value=$(__goto_resolve_path "${data[${entry_key}]}")
|
||||||
|
echo "${entry_key};${entry_value}"
|
||||||
|
done | column -t -s ';'
|
||||||
|
}
|
||||||
|
|
||||||
|
__goto_add() {
|
||||||
|
# adds or replaces an entry to the goto configuration file
|
||||||
|
# command: goto -a <directory alias>[:<directory path>]
|
||||||
|
# $1: directory alias and optional path
|
||||||
|
|
||||||
|
local dir_alias dir_path
|
||||||
|
IFS=':' read dir_alias dir_path <<< "$1"
|
||||||
|
|
||||||
|
if [[ -n "${dir_path}" ]]; then
|
||||||
|
dir_path=$(__goto_resolve_path "${dir_path}")
|
||||||
|
else
|
||||||
|
dir_path="${PWD}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
dir_alias="${dir_alias}" dir_path="${dir_path}" ${__GOTO_YQ} -i '. | (.[env(dir_alias)] = env(dir_path))' ${__GOTO_FILE}
|
||||||
|
}
|
||||||
|
|
||||||
|
__goto_remove() {
|
||||||
|
# removes an entry from the goto configuration file
|
||||||
|
# command: goto -r <directory alias>
|
||||||
|
# $1: directory alias
|
||||||
|
|
||||||
|
dir_alias="$1" ${__GOTO_YQ} -i 'del(.[env(dir_alias)])' ${__GOTO_FILE}
|
||||||
|
}
|
||||||
|
|
||||||
|
__goto_get() {
|
||||||
|
# returns the path denoted by a directory alias
|
||||||
|
# command: goto -g <directory alias>
|
||||||
|
# $1: directory alias
|
||||||
|
|
||||||
|
local dir_path="$(dir_alias="$1" ${__GOTO_YQ} '.[env(dir_alias)] // ""' ${__GOTO_FILE})"
|
||||||
|
|
||||||
|
if [[ -n "${dir_path}" ]]; then
|
||||||
|
__goto_resolve_path "${dir_path}"
|
||||||
|
else
|
||||||
|
return 10;
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
goto() {
|
||||||
|
# the goto command
|
||||||
|
|
||||||
|
local OPTIND OPT
|
||||||
|
|
||||||
|
while getopts 'a:r:g:elt' OPT; do
|
||||||
|
case "${OPT}" in
|
||||||
|
a)
|
||||||
|
__goto_add "${OPTARG}"
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
r)
|
||||||
|
__goto_remove "${OPTARG}"
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
g)
|
||||||
|
__goto_get "${OPTARG}"
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
e)
|
||||||
|
__goto_edit
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
l)
|
||||||
|
__goto_list
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
t)
|
||||||
|
__goto_test
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "goto: invalid option ${OPTARG}" >&2
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
shift $((OPTIND-1))
|
||||||
|
|
||||||
|
if (( $# > 1 )); then
|
||||||
|
echo 'goto: too many arguments' >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if (( $# == 0 )); then
|
||||||
|
cd
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
local dir_path="$(dir_alias="$1" ${__GOTO_YQ} '.[env(dir_alias)] // ""' ${__GOTO_FILE})"
|
||||||
|
|
||||||
|
if [[ -z "${dir_path}" ]]; then
|
||||||
|
echo "goto: $1: no such entry" >&2
|
||||||
|
return 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd $(__goto_resolve_path "${dir_path}")
|
||||||
|
}
|
||||||
|
|
||||||
|
# complete -F __goto_completions goto
|
||||||
|
source ${__GOTO_ROOT_DIR}/completion.bash
|
||||||
Reference in New Issue
Block a user