Commit Diff


commit - 7f623295a5073124f9fed7e368001289567d531b
commit + a6b14c928490e7ce31a46ba538047bb669d58960
blob - 4bedfb2f7529454aeab21a4cfeeefcb06e664f18
blob + d05e2e3b75353f271fdbda36dc301b9f631bb888
--- t_done.sh
+++ t_done.sh
@@ -1,137 +1,218 @@
 #! /usr/bin/env bash
 
-if [[ -z "$TODO_FILE" ]]
-then
-    printf 'Environment variable TODO_FILE not set!\n'
-    exit 3
-fi
+usage="$(cat <<'EOF'
+usage:
+    t [-aD]
+    t [-aD] [-s REGEX_STRING] [-d [INTEGER|REGEX_STRING]]
+    t /REGEX_STRING
+    t [-T] [-t [+|-]VAL[ymwd]] STRING
 
+examples:
+    t                     print incomplete todos
+    t -a                  print all todos
+    t -D                  print all done todos
+    t -s call             print all todos matching "call"
+    t /call               same as above
+    t -s "call|email"     print all todos matching "call" or "email"
+    t -D -s read          print all done todos matching "read"
+    t -d 12               mark todo item 12 as done
+    t -s read -d 3        mark todo item 3 within todos matching "read" as done
+    t -d burn             mark all todos matching "burn" as done
+    t -s burn -d .        same as above
+    t -k 7                delete todo item 7
+    t -k bunnies          delete all todos matching "bunnies"
+    t -s bunnies -k .     same as above
+    t -e                  edit $TODO_FILE in $EDITOR
+    t -T sell horse       add todo "sell horse" due today
+    t -t 20d celebrate    add todo "celebrate" due on the 20th of this month
+    t -t +1w buy racecar  add todo "buy racecar" due a week from today
+                            (for date syntax, see date manual entry)
+    t -n                  print unnumbered output (suitable for redirection)
+EOF
+)"
+
 IFS=$'\n'
-prefix='- [ ] '
+red='\e[0;31m'
+clear='\e[0m'
+str_prefix='- [ ] '
+re_todofile='[Tt][Oo][Dd][Oo](\.[^.]+)?'
+re_todo='^- \[ ] '
+re_done='^- \[[xX]] '
+re_either='^- \[[ xX]] '
 re_date='[0-9]{4}-[0-9]{2}-[0-9]{2}'
 lines=$(tput lines)
-usage='usage: t [-aD] [-s regex_match] [-d [integer|regex_match]]
-         /regexp_match
-         [-T] [-t [+|-]val[ymwd]] todo_string'
 
+for file in *
+do
+    [[ $file =~ $re_todofile ]] && todofile="$file"
+done
+
+if [[ ! $todofile && -r $TODO_FILE ]]
+then todofile="$TODO_FILE"
+elif [[ ! $todofile ]]
+then printf 'No todo file found or environment variable TODO_FILE not set!\n'
+     exit 3
+fi
+
 function t_read {
-    if [[ $showall ]]
-    then re_prefix='^- \[[ xX]] '
-    elif [[ $onlydone ]]
-    then re_prefix='^- \[[xX]] '
-    else re_prefix='^- \[ ] '
+    if [[ $onlydone ]]
+    then
+        re_prefix=$re_done
+    elif [[ $showall ]]
+    then
+        re_prefix=$re_either
+    else
+        re_prefix=$re_todo
     fi
 
-    local _casematch
-    if [[ ! $@ =~ [A-Z] ]]
-    then _casematch='--ignore-case'
-    fi
-
-    list=($(grep -E $_casematch "$re_prefix.*($*)" "$TODO_FILE"))
-    n_total=${#list[@]}
-    n_length=${#n_total}
-
-    local _due_list
-    for i in "${!list[@]}"
+    local casematch
+    [[ ! $* =~ [A-Z] ]] && casematch='--ignore-case'
+    todo_list=($(grep -E $casematch "$re_prefix.*($*)" "$todofile"))
+    local due_list
+    local item
+    for i in "${!todo_list[@]}"
     do
-        if [[ ${list[i]} =~ $re_date ]]
-        then item=${list[i]}
-             _due_list+=($item)
-             unset list[i]
+        if [[ ${todo_list[i]} =~ $re_date ]]
+        then
+            item="${todo_list[i]}"
+            due_list+=("$item")
+            unset todo_list[i]
         fi
     done
 
-    _due_list=($(printf "%s\n" "${_due_list[@]}" | sed -E "s/(.*)($re_date)(.*)/\2@@\1@@\3/" | sort -g | sed -E "s/($re_date)@@(.*)@@(.*)/\2\1\3/"))
-    list=(${_due_list[@]} ${list[@]})
+    due_list=($(printf "%s\n" "${due_list[@]}" | sed -E "s/.*($re_date).*/\1&/" | sort -g | sed -E "s/^$re_date//"))
+    todo_list=(${due_list[@]} ${todo_list[@]})
 }
 
 function t_print {
-    t_read "$@"
+    t_read "$query"
 
-    local _n=1
-    local _buffer=$(mktemp)
-    for todo in "${list[@]}"
+    local n=1
+    local buffer=$(mktemp)
+    local n_total=${#todo_list[@]}
+    local n_width=${#n_total}
+
+    for todo in "${todo_list[@]}"
     do
-        if [[ $todo =~ $re_date ]]
+        if [[ $todo =~ $re_todo && $todo =~ $re_date ]]
         then
             local date=${BASH_REMATCH//-}
             local today=$(date +%Y%m%d)
-            if (( date <= today ))
-            then todo=$(sed -E "s/($re_prefix)(.*)/\1**\2**/" <<< "$todo")
-            fi
+            (( date <= today )) && todo=$(sed -E "s/($re_prefix)(.*)/\1** \2 **/" <<< "$todo")
         fi
-        printf "%${n_length}s %s\n" $_n "${todo#- }" >> "$_buffer"
-        ((_n++))
+
+        if [[ $export ]]
+        then
+            printf "%s\n" "${todo}" >> "$buffer"
+        else
+            printf "%${n_width}s %s\n" "$n" "${todo#- }" >> "$buffer"
+        fi
+        (( n++ ))
     done
 
     if (( lines <= n_total ))
-    then ${PAGER:-less} -X < "$_buffer"
-    else cat "$_buffer"
+    then ${PAGER:-less} -X < "$buffer"
+    else cat "$buffer"
     fi
 
-    rm "$_buffer"
+    rm "$buffer"
 }
 
-function t_done {
-    t_read "$query"
-
-    local _done_list
+function t_select {
     if [[ $1 =~ ^[0-9]+$ ]]
-    then _done_list=${list[(( $1 - 1 ))]}
+    then
+        selection=${todo_list[(( $1 - 1 ))]}
     else
-        local _casematch
-        if [[ ! $@ =~ [A-Z] ]]
-        then _casematch='--ignore-case'
-        fi
-        _done_list=($(printf "%s\n" "${list[@]}" | grep $_casematch "$@" ))
+        local casematch
+        [[ ! $@ =~ [A-Z] ]] && casematch='--ignore-case'
+        selection=($(printf "%s\n" "${todo_list[@]}" | grep $casematch "$@" ))
     fi
+}
 
-    for todo in "${_done_list[@]}"
+function t_done {
+    t_read "$query"
+    t_select "$@"
+
+    for todo in "${selection[@]}"
     do
-        todo=${todo#- \[ \] }
         todo=$(sed 's/[][\/$*.^|]/\\&/g' <<< "$todo")
-        sed -i '' "/$todo/ s/^- \[ ]/- \[X]/" "$TODO_FILE"
+        sed -i '' "/$todo/s/^- \[ ]/- \[X]/" "$todofile"
     done
 }
 
-while getopts ':aDs:d:Tt:he' opt
+function t_kill {
+    t_read "$query"
+    t_select "$@"
+
+    for todo in "${selection[@]}"
+    do
+        todo=$(sed 's/[][\/$*.^|]/\\&/g' <<< "$todo")
+        sed -i '' "/$todo/d" "$todofile"
+    done
+}
+
+function t_toggle {
+    t_read "$query"
+    t_select "$@"
+
+    for todo in "${selection[@]}"
+    do
+        if [[ $todo =~ $re_done ]]
+        then
+            todo=$(sed 's/[][\/$*.^|]/\\&/g' <<< "$todo")
+            sed -i '' "/$todo/s/^- \[[xX]]/- [ ]/" "$todofile"
+        elif [[ $todo =~ $re_todo ]]
+        then
+            todo=$(sed 's/[][\/$*.^|]/\\&/g' <<< "$todo")
+            sed -i '' "/$todo/s/^- \[ ]/- [X]/" "$todofile"
+        fi
+    done
+}
+
+while getopts ':heaDns:k:d:z:Tt:' opt
 do
     case $opt in
-        a) showall=0
-           ;;
-        D) onlydone=0
-           ;;
-        s) query=$OPTARG
-           ;;
-        d) markdone=$OPTARG
-           ;;
-        T) due=" $(date +%F)"
-           ;;
-        t) due=" $(date -v $OPTARG +%F)"
-           ;;
         h) printf "%s\n" "$usage"
-           exit 0
-           ;;
-        e) ${EDITOR:-nano} "$target"
-           exit 0
-           ;;
-        :) printf "Option -%s requires an argument\n" "$OPTARG"
-           exit 2
+           exit 0;;
+        e) ${EDITOR:-nano} "$todofile"
+           exit 0;;
+        a) showall=0;;
+        D) onlydone=0;;
+        n) export=0;;
+        s) query=$OPTARG;;
+        k) kill=$OPTARG;;
+        d) markdone=$OPTARG;;
+        z) toggle=$OPTARG;;
+        T) due=" $(date +%F)";;
+        t) due=" $(date -v $OPTARG +%F)";;
+        :) printf "t: option -%s requires an argument\n" "$OPTARG"
+           exit 2;;
+        *) printf "t: unrecognized option -%s\n\n" "$OPTARG"
+           printf "%s\n" "$usage"
+           exit 1;;
     esac
 done
 
 shift $(( OPTIND - 1 ))
 
-if [[ $@ =~ ^\/ ]]
-then query=${*#/}
-fi
+[[ $@ =~ ^\/ ]] && query="${*#/}"
 
 if [[ -n $markdone ]]
-then t_done "$markdone"
+then
+    t_done "$markdone"
+elif [[ -n $toggle ]]
+then
+    t_toggle "$toggle"
+elif [[ -n $kill ]]
+then
+    t_kill "$kill"
 elif [[ -n $query ]]
-then t_print "$query"
+then
+    t_print "$query"
 elif [[ -n $@ ]]
-then todo="$prefix$*$due"
-     echo $todo >> "$TODO_FILE"
-else t_print
+then
+    todo="$str_prefix$*$due"
+    echo $todo >> "$todofile"
+else
+    t_print
 fi