diff options
| author | Robert Günzler <r@gnzler.io> | 2026-04-08 13:18:14 +0900 |
|---|---|---|
| committer | Robert Günzler <r@gnzler.io> | 2026-04-08 13:18:14 +0900 |
| commit | b61b58ce2c1371d6f904f108fa7feed3abfbe5c7 (patch) | |
| tree | 008a5cf54ecbfffb97ddac30e44c8ab224395ab0 /bin/todo | |
| parent | d4620f3ac119263fee90bd819eadaf84fc8d50b7 (diff) | |
*
Automated-commit-by: dots Signed-off-by: Robert Günzler <r@gnzler.io>
Diffstat (limited to 'bin/todo')
| -rwxr-xr-x | bin/todo | 216 |
1 files changed, 216 insertions, 0 deletions
diff --git a/bin/todo b/bin/todo new file mode 100755 index 0000000..7436779 --- /dev/null +++ b/bin/todo @@ -0,0 +1,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 $@ |