Blame


1 c7704fb6 2023-11-15 rnkn #! /usr/bin/env bash
2 c7704fb6 2023-11-15 rnkn
3 c7704fb6 2023-11-15 rnkn usage() {
4 c7704fb6 2023-11-15 rnkn cat <<EOF
5 c7704fb6 2023-11-15 rnkn usage:
6 c7704fb6 2023-11-15 rnkn t [-aD]
7 c7704fb6 2023-11-15 rnkn t [-aD] [-s REGEX_STRING] [-d [INTEGER|REGEX_STRING]]
8 c7704fb6 2023-11-15 rnkn t /REGEX_STRING
9 c7704fb6 2023-11-15 rnkn t [-T] [-t [+|-]VAL[ymwd]] STRING
10 c7704fb6 2023-11-15 rnkn
11 c7704fb6 2023-11-15 rnkn examples:
12 c7704fb6 2023-11-15 rnkn t print incomplete todos
13 c7704fb6 2023-11-15 rnkn t -a print all todos
14 c7704fb6 2023-11-15 rnkn t -D print all done todos
15 c7704fb6 2023-11-15 rnkn t -s call print all todos matching "call"
16 c7704fb6 2023-11-15 rnkn t /call same as above
17 c7704fb6 2023-11-15 rnkn t -s "call|email" print all todos matching "call" or "email"
18 c7704fb6 2023-11-15 rnkn t -D -s read print all done todos matching "read"
19 c7704fb6 2023-11-15 rnkn t -d 12 mark todo item 12 as done
20 c7704fb6 2023-11-15 rnkn t -s read -d 3 mark todo item 3 within todos matching "read" as done
21 c7704fb6 2023-11-15 rnkn t -d burn mark all todos matching "burn" as done
22 c7704fb6 2023-11-15 rnkn t -s burn -d . same as above
23 c7704fb6 2023-11-15 rnkn t -k 7 delete todo item 7
24 c7704fb6 2023-11-15 rnkn t -k bunnies delete all todos matching "bunnies"
25 c7704fb6 2023-11-15 rnkn t -s bunnies -k . same as above
26 c7704fb6 2023-11-15 rnkn t -e edit $TODO_FILE in $EDITOR
27 c7704fb6 2023-11-15 rnkn t -T sell horse add todo "sell horse" due today
28 c7704fb6 2023-11-15 rnkn t -t 20d celebrate add todo "celebrate" due on the 20th of this month
29 c7704fb6 2023-11-15 rnkn t -t +1w buy racecar add todo "buy racecar" due a week from today
30 c7704fb6 2023-11-15 rnkn (for date syntax, see date manual entry)
31 c7704fb6 2023-11-15 rnkn t -n print unnumbered output (suitable for redirection)
32 c7704fb6 2023-11-15 rnkn EOF
33 c7704fb6 2023-11-15 rnkn exit 1
34 c7704fb6 2023-11-15 rnkn }
35 c7704fb6 2023-11-15 rnkn
36 c7704fb6 2023-11-15 rnkn IFS=$'\n'
37 c7704fb6 2023-11-15 rnkn red='\e[0;31m'
38 c7704fb6 2023-11-15 rnkn clear='\e[0m'
39 c7704fb6 2023-11-15 rnkn str_prefix='- [ ] '
40 c7704fb6 2023-11-15 rnkn re_todofile='[Tt][Oo][Dd][Oo](\.[^.]+)?'
41 c7704fb6 2023-11-15 rnkn re_todo='^- \[ ] '
42 c7704fb6 2023-11-15 rnkn re_done='^- \[[xX]] '
43 c7704fb6 2023-11-15 rnkn re_either='^- \[[ xX]] '
44 c7704fb6 2023-11-15 rnkn re_date='[0-9]{4}-[0-9]{2}-[0-9]{2}'
45 c7704fb6 2023-11-15 rnkn lines=$(tput lines)
46 c7704fb6 2023-11-15 rnkn
47 c7704fb6 2023-11-15 rnkn for file in *
48 c7704fb6 2023-11-15 rnkn do
49 c7704fb6 2023-11-15 rnkn [[ $file =~ $re_todofile ]] && todofile="$file"
50 c7704fb6 2023-11-15 rnkn done
51 c7704fb6 2023-11-15 rnkn
52 c7704fb6 2023-11-15 rnkn if [[ ! $todofile && -r $TODO_FILE ]]
53 c7704fb6 2023-11-15 rnkn then todofile="$TODO_FILE"
54 c7704fb6 2023-11-15 rnkn elif [[ ! $todofile ]]
55 c7704fb6 2023-11-15 rnkn then printf 'No todo file found or environment variable TODO_FILE not set!\n'
56 c7704fb6 2023-11-15 rnkn exit 3
57 c7704fb6 2023-11-15 rnkn fi
58 c7704fb6 2023-11-15 rnkn
59 c7704fb6 2023-11-15 rnkn t_read() {
60 c7704fb6 2023-11-15 rnkn if [[ $onlydone ]]
61 c7704fb6 2023-11-15 rnkn then
62 c7704fb6 2023-11-15 rnkn re_prefix=$re_done
63 c7704fb6 2023-11-15 rnkn elif [[ $showall ]]
64 c7704fb6 2023-11-15 rnkn then
65 c7704fb6 2023-11-15 rnkn re_prefix=$re_either
66 c7704fb6 2023-11-15 rnkn else
67 c7704fb6 2023-11-15 rnkn re_prefix=$re_todo
68 c7704fb6 2023-11-15 rnkn fi
69 c7704fb6 2023-11-15 rnkn
70 c7704fb6 2023-11-15 rnkn local casematch
71 c7704fb6 2023-11-15 rnkn [[ ! $* =~ [A-Z] ]] && casematch='-i'
72 c7704fb6 2023-11-15 rnkn todo_list=($(grep -E $casematch "$re_prefix.*($*)" "$todofile"))
73 c7704fb6 2023-11-15 rnkn local due_list
74 c7704fb6 2023-11-15 rnkn local item
75 c7704fb6 2023-11-15 rnkn for i in "${!todo_list[@]}"
76 c7704fb6 2023-11-15 rnkn do
77 c7704fb6 2023-11-15 rnkn if [[ ${todo_list[i]} =~ $re_date ]]
78 c7704fb6 2023-11-15 rnkn then
79 c7704fb6 2023-11-15 rnkn item="${todo_list[i]}"
80 c7704fb6 2023-11-15 rnkn due_list+=("$item")
81 c7704fb6 2023-11-15 rnkn unset todo_list[i]
82 c7704fb6 2023-11-15 rnkn fi
83 c7704fb6 2023-11-15 rnkn done
84 c7704fb6 2023-11-15 rnkn
85 c7704fb6 2023-11-15 rnkn due_list=($(printf "%s\n" "${due_list[@]}" | sed -E "s/.*($re_date).*/\1&/" | sort -g | sed -E "s/^$re_date//"))
86 c7704fb6 2023-11-15 rnkn todo_list=(${due_list[@]} ${todo_list[@]})
87 c7704fb6 2023-11-15 rnkn }
88 c7704fb6 2023-11-15 rnkn
89 c7704fb6 2023-11-15 rnkn t_print() {
90 c7704fb6 2023-11-15 rnkn t_read "$query"
91 c7704fb6 2023-11-15 rnkn
92 c7704fb6 2023-11-15 rnkn local n=1
93 c7704fb6 2023-11-15 rnkn local buffer=$(mktemp)
94 c7704fb6 2023-11-15 rnkn local n_total=${#todo_list[@]}
95 c7704fb6 2023-11-15 rnkn local n_width=${#n_total}
96 c7704fb6 2023-11-15 rnkn
97 c7704fb6 2023-11-15 rnkn for todo in "${todo_list[@]}"
98 c7704fb6 2023-11-15 rnkn do
99 c7704fb6 2023-11-15 rnkn if [[ $todo =~ $re_todo && $todo =~ $re_date ]]
100 c7704fb6 2023-11-15 rnkn then
101 c7704fb6 2023-11-15 rnkn local date=${BASH_REMATCH//-}
102 c7704fb6 2023-11-15 rnkn local today=$(date +%Y%m%d)
103 c7704fb6 2023-11-15 rnkn (( date <= today )) && todo=$(sed -E "s/($re_prefix)(.*)/\1** \2 **/" <<< "$todo")
104 c7704fb6 2023-11-15 rnkn fi
105 c7704fb6 2023-11-15 rnkn
106 c7704fb6 2023-11-15 rnkn if [[ $export ]]
107 c7704fb6 2023-11-15 rnkn then
108 c7704fb6 2023-11-15 rnkn printf "%s\n" "${todo}" >> "$buffer"
109 c7704fb6 2023-11-15 rnkn else
110 c7704fb6 2023-11-15 rnkn printf "%${n_width}s %s\n" "$n" "${todo#- }" >> "$buffer"
111 c7704fb6 2023-11-15 rnkn fi
112 c7704fb6 2023-11-15 rnkn (( n++ ))
113 c7704fb6 2023-11-15 rnkn done
114 c7704fb6 2023-11-15 rnkn
115 c7704fb6 2023-11-15 rnkn if (( lines <= n_total ))
116 c7704fb6 2023-11-15 rnkn then ${PAGER:-less} -X < "$buffer"
117 c7704fb6 2023-11-15 rnkn else cat "$buffer"
118 c7704fb6 2023-11-15 rnkn fi
119 c7704fb6 2023-11-15 rnkn
120 c7704fb6 2023-11-15 rnkn rm "$buffer"
121 c7704fb6 2023-11-15 rnkn }
122 c7704fb6 2023-11-15 rnkn
123 c7704fb6 2023-11-15 rnkn t_select() {
124 c7704fb6 2023-11-15 rnkn if [[ $1 =~ ^[0-9]+$ ]]
125 c7704fb6 2023-11-15 rnkn then
126 c7704fb6 2023-11-15 rnkn selection=${todo_list[(( $1 - 1 ))]}
127 c7704fb6 2023-11-15 rnkn else
128 c7704fb6 2023-11-15 rnkn local casematch
129 c7704fb6 2023-11-15 rnkn [[ ! $@ =~ [A-Z] ]] && casematch='-i'
130 c7704fb6 2023-11-15 rnkn selection=($(printf "%s\n" "${todo_list[@]}" | grep $casematch "$@" ))
131 c7704fb6 2023-11-15 rnkn fi
132 c7704fb6 2023-11-15 rnkn }
133 c7704fb6 2023-11-15 rnkn
134 c7704fb6 2023-11-15 rnkn t_done() {
135 c7704fb6 2023-11-15 rnkn t_read "$query"
136 c7704fb6 2023-11-15 rnkn t_select "$1"
137 c7704fb6 2023-11-15 rnkn
138 c7704fb6 2023-11-15 rnkn for todo in "${selection[@]}"
139 c7704fb6 2023-11-15 rnkn do
140 c7704fb6 2023-11-15 rnkn todo=$(sed 's/[][\/$*.^|]/\\&/g' <<< "$todo")
141 c7704fb6 2023-11-15 rnkn sed -i'' "/$todo/s/^- \[ ]/- \[X]/" "$todofile"
142 c7704fb6 2023-11-15 rnkn done
143 c7704fb6 2023-11-15 rnkn }
144 c7704fb6 2023-11-15 rnkn
145 c7704fb6 2023-11-15 rnkn t_kill() {
146 c7704fb6 2023-11-15 rnkn t_read "$query"
147 c7704fb6 2023-11-15 rnkn t_select "$1"
148 c7704fb6 2023-11-15 rnkn
149 c7704fb6 2023-11-15 rnkn for todo in "${selection[@]}"
150 c7704fb6 2023-11-15 rnkn do
151 c7704fb6 2023-11-15 rnkn todo=$(sed 's/[][\/$*.^|]/\\&/g' <<< "$todo")
152 c7704fb6 2023-11-15 rnkn sed -i '' "/$todo/d" "$todofile"
153 c7704fb6 2023-11-15 rnkn done
154 c7704fb6 2023-11-15 rnkn }
155 c7704fb6 2023-11-15 rnkn
156 c7704fb6 2023-11-15 rnkn t_toggle() {
157 c7704fb6 2023-11-15 rnkn t_read "$query"
158 c7704fb6 2023-11-15 rnkn t_select "$1"
159 c7704fb6 2023-11-15 rnkn
160 c7704fb6 2023-11-15 rnkn for todo in "${selection[@]}"
161 c7704fb6 2023-11-15 rnkn do
162 c7704fb6 2023-11-15 rnkn if [[ $todo =~ $re_done ]]
163 c7704fb6 2023-11-15 rnkn then
164 c7704fb6 2023-11-15 rnkn todo=$(sed 's/[][\/$*.^|]/\\&/g' <<< "$todo")
165 c7704fb6 2023-11-15 rnkn sed -i '' "/$todo/s/^- \[[xX]]/- [ ]/" "$todofile"
166 c7704fb6 2023-11-15 rnkn elif [[ $todo =~ $re_todo ]]
167 c7704fb6 2023-11-15 rnkn then
168 c7704fb6 2023-11-15 rnkn todo=$(sed 's/[][\/$*.^|]/\\&/g' <<< "$todo")
169 c7704fb6 2023-11-15 rnkn sed -i '' "/$todo/s/^- \[ ]/- [X]/" "$todofile"
170 c7704fb6 2023-11-15 rnkn fi
171 c7704fb6 2023-11-15 rnkn done
172 c7704fb6 2023-11-15 rnkn }
173 c7704fb6 2023-11-15 rnkn
174 c7704fb6 2023-11-15 rnkn t_openurl() {
175 c7704fb6 2023-11-15 rnkn t_read "$query"
176 c7704fb6 2023-11-15 rnkn t_select "$1"
177 c7704fb6 2023-11-15 rnkn
178 c7704fb6 2023-11-15 rnkn urls=($(printf "%s\n" "${selection[@]}" | grep -Eo "https?://[^ ]+"))
179 c7704fb6 2023-11-15 rnkn for url in "${urls[@]}"
180 c7704fb6 2023-11-15 rnkn do
181 c7704fb6 2023-11-15 rnkn open "$url" && echo "t: opening ${url} ..."
182 c7704fb6 2023-11-15 rnkn done
183 c7704fb6 2023-11-15 rnkn }
184 c7704fb6 2023-11-15 rnkn
185 c7704fb6 2023-11-15 rnkn while getopts ':heaDns:k:d:z:u:Tt:' opt
186 c7704fb6 2023-11-15 rnkn do
187 c7704fb6 2023-11-15 rnkn case $opt in
188 c7704fb6 2023-11-15 rnkn (h) usage ;;
189 c7704fb6 2023-11-15 rnkn (e) ${EDITOR:-vi} "$todofile"
190 c7704fb6 2023-11-15 rnkn exit 0;;
191 c7704fb6 2023-11-15 rnkn (a) showall=0;;
192 c7704fb6 2023-11-15 rnkn (D) onlydone=0;;
193 c7704fb6 2023-11-15 rnkn (n) export=0;;
194 c7704fb6 2023-11-15 rnkn (s) query=$OPTARG;;
195 c7704fb6 2023-11-15 rnkn (k) kill=$OPTARG;;
196 c7704fb6 2023-11-15 rnkn (d) markdone=$OPTARG;;
197 c7704fb6 2023-11-15 rnkn (z) toggle=$OPTARG;;
198 c7704fb6 2023-11-15 rnkn (u) openurl=$OPTARG;;
199 c7704fb6 2023-11-15 rnkn (T) due=" $(date +%F)";;
200 c7704fb6 2023-11-15 rnkn (t) due=" $(date -v $OPTARG +%F)";;
201 c7704fb6 2023-11-15 rnkn (:) printf "t: option -%s requires an argument\n" "$OPTARG"
202 c7704fb6 2023-11-15 rnkn exit 2 ;;
203 c7704fb6 2023-11-15 rnkn (*) printf "t: unrecognized option -%s\n\n" "$OPTARG"
204 c7704fb6 2023-11-15 rnkn usage ;;
205 c7704fb6 2023-11-15 rnkn esac
206 c7704fb6 2023-11-15 rnkn done
207 c7704fb6 2023-11-15 rnkn
208 c7704fb6 2023-11-15 rnkn shift "$(( OPTIND - 1 ))"
209 c7704fb6 2023-11-15 rnkn
210 c7704fb6 2023-11-15 rnkn [[ $@ =~ ^\/ ]] && query="${*#/}"
211 c7704fb6 2023-11-15 rnkn
212 c7704fb6 2023-11-15 rnkn if [[ -n $openurl ]]
213 c7704fb6 2023-11-15 rnkn then
214 c7704fb6 2023-11-15 rnkn t_openurl "$openurl"
215 c7704fb6 2023-11-15 rnkn elif [[ -n $markdone ]]
216 c7704fb6 2023-11-15 rnkn then
217 c7704fb6 2023-11-15 rnkn t_done "$markdone"
218 c7704fb6 2023-11-15 rnkn elif [[ -n $toggle ]]
219 c7704fb6 2023-11-15 rnkn then
220 c7704fb6 2023-11-15 rnkn t_toggle "$toggle"
221 c7704fb6 2023-11-15 rnkn elif [[ -n $kill ]]
222 c7704fb6 2023-11-15 rnkn then
223 c7704fb6 2023-11-15 rnkn t_kill "$kill"
224 c7704fb6 2023-11-15 rnkn elif [[ -n $query ]]
225 c7704fb6 2023-11-15 rnkn then
226 c7704fb6 2023-11-15 rnkn t_print "$query"
227 c7704fb6 2023-11-15 rnkn elif [[ -n $@ ]]
228 c7704fb6 2023-11-15 rnkn then
229 c7704fb6 2023-11-15 rnkn todo="$str_prefix$*$due"
230 c7704fb6 2023-11-15 rnkn echo $todo >> "$todofile"
231 c7704fb6 2023-11-15 rnkn else
232 c7704fb6 2023-11-15 rnkn t_print
233 c7704fb6 2023-11-15 rnkn fi