#!/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 AGENDA_DIR="${XDG_DATA_HOME:-"$HOME/.local/share"}/agenda" 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