1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
|
#!/bin/bash
set -e
[ -n "$DEBUG" ] && set -x
_todo_files=("todo.txt" "TODO")
_todo_file=""
function has_todo_file() {
cwd=$(pwd)
for f in "${_todo_files[@]}"; do
test -f "$cwd/$f" && _todo_file="$cwd/$f" && return 0
done
return 1
}
function print_org() {
awk '
function filename() {
n = split(FILENAME, a, "/")
return a[n]
}
function res() {
return "\033[0m"
}
function bold(s) {
return "\033[1m" s "\033[22m"
}
function strike(s) {
return "\033[9m" s "\033[29m"
}
function red(s) {
return "\033[31m" s res()
}
function green(s) {
return "\033[32m" s res()
}
function blue(s) {
return "\033[34m" s res()
}
function magenta(s) {
return "\033[35m" s res()
}
function osc8(uri, s) {
return "\033]8;;" uri "\033\\\\" s "\033]8;;\033\\"
}
function link(uri) {
return osc8(uri, "\033[2m" uri res())
}
{
if ($1 ~ /^#/) next
// support todotxt files
if (filename() == "todo.txt") {
status = "TODO"
if ($1 ~ /^x/) {
status = "DONE"
$0 = gensub(/^[x] /, "", "1")
$0 = strike($0)
}
$0 = gensub(/ (\+[^ ]+)/, bold(blue(" \\1")), "g")
$0 = gensub(/ (@[^ ]+)/, bold(blue(" \\1")), "g")
$0 = gensub(/(https?:\/\/[^ ]+)/, link("\\1"), "g")
tasks[status][i++] = $0
next
}
// support TODO files
if ($1 ~ /^(-|*)/) {
// detect status strings
match($0, /\s(TODO|WIP|WAITING|DONE)\s/, arr)
if (filename() == "TODO" && arr[1] == "") {
$0 = gensub(/(^[\w]*(-|\*))/, "\\1 TODO", "1")
}
$0 = gensub(/(TODO)/, red("\\1"), "1")
$0 = gensub(/(WIP)/, blue("\\1"), "1")
$0 = gensub(/(DONE)/, green("\\1"), "1")
$0 = gensub(/(\?\?\?)/, bold(magenta("\\1")), "1")
tasks[arr[1]][i++] = $0
}
}
END {
order[1] = "TODO"
order[2] = "WIP"
order[3] = "DONE"
for (i in order) {
for (t in tasks[order[i]]) {
print " " tasks[order[i]][t]
}
}
}' $1
}
function print_hr() {
local str cols
local start line end
str="$1"
[[ -n $str ]] && str=" $str "
cols=$((${COLUMNS:-$(tput cols)} - ${#str} - 3))
start=$'\e(0'
end=$'\e(B'
line="qqq"
while ((${#line} < $cols)); do line+=$line; done
printf "╾"
printf "%s%s%s" "$start" "${line:0:$((cols / 2))}" "$end"
printf "%s%s%s" "$start" "${line:0:$((cols / 2))}" "$end"
printf "%s" "$str"
printf "%s%s%s" "$start" "${line:0:1}" "$end"
printf "╼"
printf "\n"
}
function print_shell_hook() {
case $1 in
bash)
cat <<-EOF
# Hook to run todo on directory change.
__todo_oldpwd="\$(\builtin pwd -L)"
function __todo_hook() {
\builtin local -r retval="$?"
\builtin local pwd_tmp
pwd_tmp="\$(\builtin pwd -L)"
if [[ \${__todo_oldpwd} != "\${pwd_tmp}" ]]; then
__todo_oldpwd="\${pwd_tmp}"
\command todo
fi
return "\${retval}"
}
# Initialize hook.
if [[ \${PROMPT_COMMAND:=} != *'__todo_hook'* ]]; then
PROMPT_COMMAND="__todo_hook;\${PROMPT_COMMAND#;}"
fi
# Run at startup.
\command sleep .1 && \command todo
EOF
;;
zsh)
cat <<-EOF
__todo_oldpwd="$(\builtin pwd -L)"
function __todo_hook() {
\builtin local -r retval="$?"
\builtin local pwd_tmp
if [[ \${__todo_oldpwd} != "\${pwd_tmp}" ]]; then
__todo_oldpwd="\${pwd_tmp}"
\command ${0}
fi
return "\${retval}"
}
typeset -ag precmd_functions;
if [[ -z \${precmd_functions[(r)__todo_hook]} ]]; then
precmd_functions+=__todo_hook;
fi
EOF
;;
cd)
echo "cd() { builtin cd \"\$@\" && $0; }"
;;
-h | --help | *)
echo "usage: todo hook <integration>"
echo ""
echo "use like this: eval \$(todo hook <integration>)"
echo ""
echo "supported integrations:"
echo " - zsh: run todo as a precmd hook"
echo " (this can get quite annoying when you're in a direcory that has a todo file)"
echo " - cd: run todo as a post-cd hook"
echo ""
exit 1
;;
esac
}
function main() {
case "$1" in
hook)
shift
print_shell_hook "$1"
;;
-h | --help)
echo "usage: todo [-r] [hook <integration>]"
echo "A quite stupid todo/agenda script that reminds you of things you need to do"
echo ""
echo " -r, --recursive walk the file tree, searching for todos"
echo " <integration> shell integration, you should eval this"
echo ""
exit 0
;;
-r | --recursive | -R)
while read FILE; do
print_hr "[$FILE]"
print_org $FILE
done < <(rg --files ${_todo_files[@]/#/-g })
;;
*)
if [ -f "$1" ]; then
print_hr "[$1]"
print_org $1
exit 0
fi
if has_todo_file; then
print_hr "[$(basename $_todo_file)]"
print_org $_todo_file
print_hr
fi
;;
esac
}
main $@
|