feat(completion): add bash completion

Still missing a few options and cannot complete dates inside string
delimiters, but mostly works.
This commit is contained in:
2023-11-15 01:47:30 +01:00
parent 5a483edbbe
commit fbea958363
2 changed files with 194 additions and 2 deletions

190
completion.bash Normal file
View File

@@ -0,0 +1,190 @@
#!/usr/bin/env bash
# Bash completion script for the agenda(1) script.
# TODO: Maybe its easier to add an api to the agenda script to query for the
# available -t option values?
if [ -z "$AGENDA_DIR" ]; then
if type xdg-user-dir >/dev/null 2>&1; then
AGENDA_DIR="$(xdg-user-dir DOCUMENTS)/agenda"
else
AGENDA_DIR="$HOME/.agenda"
fi
fi
AGENDA_COMP_DATE_MOD=("next" "last")
AGENDA_COMP_DATE_UNIT=("day" "week" "month" "year")
AGENDA_COMP_DATE_DAY=("monday" "tuesday" "wednesday" "thursday" "friday" "saturday" "sunday")
AGENDA_COMP_DATE_REL=("yesterday" "today" "tomorrow")
AGENDA_COMP_DATE=(
"${AGENDA_COMP_DATE_MOD[@]}"
"${AGENDA_COMP_DATE_UNIT[@]}"
"${AGENDA_COMP_DATE_DAY[@]}"
"${AGENDA_COMP_DATE_REL[@]}"
)
__agenda_comp() {
# Pointer to current and last completion words.
local cur last
cur=${COMP_WORDS[COMP_CWORD]}
last=${COMP_WORDS[COMP_CWORD - 1]}
# All subcommands take a date (or parts of a date) as their final arguments.
if __agenda_comp_is_date_word "${COMP_WORDS[-2]}"; then
return
fi
if __agenda_comp_is_date_mod "$last"; then
local words=("${AGENDA_COMP_DATE_UNIT[@]}" "${AGENDA_COMP_DATE_DAY[@]}")
mapfile -t COMPREPLY < <(compgen -W "${words[*]}" -- "$cur")
return
fi
case ${COMP_WORDS[1]} in
"edit")
mapfile -t COMPREPLY < <(__agenda_comp_edit "$last" "$cur")
;;
"list"|"ls")
mapfile -t COMPREPLY < <(__agenda_comp_list "$last" "$cur")
;;
*)
if (( COMP_CWORD == 1 )); then
local dates
mapfile -t dates < <(__agenda_comp_iso_date "$cur")
local words=(
"edit"
"list"
"${dates[@]}"
"${AGENDA_COMP_DATE[@]}"
"${AGENDA_COMP_DATE_DAY[@]}"
)
mapfile -t COMPREPLY < <(compgen -W "${words[*]}" -- "${COMP_WORDS[1]}")
else
# The "edit" subcommand is implicit.
mapfile -t COMPREPLY < <(__agenda_comp_edit "$last" "$cur")
fi
;;
esac
}
complete -F __agenda_comp agenda
__agenda_comp_edit() {
local last="$1" cur="$2"
case $last in
"-"*"t")
compgen -W "$(__agenda_comp_names)" -- "$cur"
;;
*)
__agenda_comp_opt "c" "$cur"
__agenda_comp_opt "E" "$cur"
__agenda_comp_opt "t" "$cur"
compgen -W "${AGENDA_COMP_DATE[*]} $(__agenda_comp_iso_date "$cur")" -- "$cur"
;;
esac
}
__agenda_comp_list() {
local last="$1" cur="$2"
case $last in
# TODO: Somehow complete date wrapped in a string?
"-"*"u")
local year
year=$(printf '%(%Y)T' -1)
compgen -W "$(__agenda_comp_iso_date "$cur" "$year")" -- "$cur"
;;
"-"*"t")
compgen -W "$(__agenda_comp_names)" -- "$cur"
;;
*)
__agenda_comp_opt "a" "$cur"
__agenda_comp_opt "c" "$cur"
__agenda_comp_opt "u" "$cur"
__agenda_comp_opt "t" "$cur"
compgen -W "${AGENDA_COMP_DATE[*]} $(__agenda_comp_iso_date "$cur")" -- "$cur"
;;
esac
}
__agenda_comp_is_date_mod() {
[[ $last == "next" ]] || [[ $last == "last" ]] || [[ $last =~ ^[-+]?[0-9]+$ ]]
}
__agenda_comp_is_date_word() {
local word words=(
"${AGENDA_COMP_DATE_DAY[@]}"
"${AGENDA_COMP_DATE_UNIT[@]}"
"${AGENDA_COMP_DATE_REL[@]}"
)
for word in "${words[@]}"; do
if [[ $1 == "$word" ]]; then
return 0
fi
done
if [[ $1 =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]]; then
return 0
fi
return 1
}
__agenda_comp_names() {
local dir
for dir in "$AGENDA_DIR"/*; do
if [ -e "$dir" ]; then
echo "${dir##*/}"
fi
done
}
# TODO: Complete years only without appending a space on completion (If
# possible, idk)?
__agenda_comp_iso_date() {
local year month day
if [[ $1 =~ ^[0-9]{4}-? ]]; then
year=${1%%-*}
elif [[ -n ${2:-} ]]; then
year=$2
else
return
fi
for month in {1..12}; do
case $month in
1|3|5|7|8|10|12)
for day in {1..31}; do
printf "%04d-%02d-%02d\n" "$year" "$month" "$day"
done
;;
4|6|9|11)
for day in {1..30}; do
printf "%04d-%02d-%02d\n" "$year" "$month" "$day"
done
;;
2)
# TODO: Leap years.
for day in {1..28}; do
printf "%04d-%02d-%02d\n" "$year" "$month" "$day"
done
;;
esac
done
}
__agenda_comp_opt() {
local regex="^.*[[:space:]]-.*$1.*.*$"
if ! [[ $COMP_LINE =~ $regex ]]; then
compgen -W "-$1" -- "$2"
fi
}
# vi: noet sts=4 sw=4 tw=84