My Todo Manager

Calcurse

In my previous post about my Calender Setup, I mentioned about how I was using Calcurse as my calender. Now calcurse also has a Todo section which works great, except that I have to open calcurse to use it. I just want to quickly see what I need to do, press enter to say I am done with it and it should be gone. Adding an entry should be just as easy

Enter dmenu

dmenu is a suckless menu selector which has fuzzy search (patch), default text (patch) and autocomplete. This is everything I need. So I made a script to use dmenu as my So I simply read the calcurse todo file and display a menu with each entry in the todo as a dmenu entry.

When user inputs, I check if it matches any previous entry. If it does, then I remove the entry from the todo file.

When the user input does not match any entry, it is assumed to be a new entry, and added to the file.

Rounding off edges

This solution has 2 problems,

  1. Calcurse uses a numbered ranking to signify the urgency of the entry. But adding a new entry at the default urgency is hard to type: [0] Something I have to do. So if the new entry input doesn’t start with \[.\], the script just adds [0]
  2. There is no way to edit an entry. So I changed the script to put back the last deleted entry into the prompt. So an edit is actually a delete followed by a re-entry. If you really wanted to delete an entry, then just hit ctrl-u to clear the line.

Tying it all up together

I added a keybind to dwm to run my script, and everything works so beautifully. Now I can go and remove the todo entry to write this post.

Final script

Here is the final script-

#!/bin/zsh
#
# Write/remove a task to do later.
#
# Select an existing entry to remove it from the file, or type a new entry to
# add it.
#

file="$HOME/.calcurse/todo"
touch "$file"
height=$(wc -l "$file" | awk '{print $1}')
prompt="Add/delete a task: "

cmd=$(sort -r "$file" | dmenu -l "$height" -p "$prompt" -i )
while [ -n "$cmd" ]; do
  if grep -qF "$cmd" "$file"; then
        grep -vF "$cmd" "$file" > "$file.new" # This selects all the lines that DONT match
        mv "$file.new" "$file"
    (( height = height - 1 ))
    else
    if [[ "$(echo $cmd | tr -d \\n)" =~ "^\[[[:digit:]]\]" ]]; then
      echo "$cmd" >> "$file"
    else
      echo "[0] $cmd" >> "$file"
    fi
    cmd=""
    (( height = height + 1 ))
    fi

  cmd=$(sort -r "$file" | dmenu -l "$height" -p "$prompt" -it "$cmd" "$@" )
done

exit 0
#mysetup, #mycreation, #todoman, #suckless