every #todo is a file #shell #sh
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/sh | |
# Author: Jakukyo Friel <weakish@gmail,com> | |
# License: GPL v2 | |
# ftm -- an 'every todo is a file' todo manager | |
# ========================================================= | |
# Description | |
# ------------------------------------------------------- | |
# | |
# Ftm is a simple todo manager using a 'one todo item per file' approach. | |
# | |
# Every todo item is a file. | |
# Ftm stores todo items in a directory. | |
# Ftm uses the file name to record the todo item. | |
# If you want to add some notes, you simply write them down in the file. | |
# You can access your todo items via ftm's interface, or directly | |
# using commands like | |
# `mv`, `touch`, `rm`, etc. | |
# Installation | |
# ------------------------------------------------------- | |
# | |
# Ftm is written in one single shell script. | |
# You may simply put it in your `PATH`, for example, `~/bin`. | |
# Upgrade from old version | |
# ---------------------------- | |
# If you use a todo directory other than the default one, | |
# you need to run `ftm --init yourdir` again after installing | |
# the new version. | |
# Versions 0.0.x uses ~/.config/ftm as the default directory. | |
# Ftm defaults to ~/.ftm now. | |
# Thus if you upgrade from 0.0.x and uses the default directory, | |
# you need to run `ftm --init ~/.config/ftm` (if you want to | |
# continue to use it) or `mv ~/.config/ftm ~/.ftm`. | |
# Quickstart | |
# ------------------------------------------------------- | |
# Run `ftm init`. | |
# Use `ftm -a` to add a todo, do it, and `ftm -d` | |
# to mark it done. | |
# Use `ftm -l` to list todos. | |
# Usage | |
# ----------------- | |
help() { | |
cat <<ENDHELP | |
ftm -- an 'every todo is a file' todo manager | |
list of options: | |
-a, --add add a new todo | |
-c, --cat [todo] show todo note | |
-e, --edit [todo] edit todos | |
-d, --done [todo] mark todos as completed | |
-h, --help print this help page | |
--init [dir] init todo directory | |
-l, --list [word] list todos | |
-n, --next show next action | |
-p, --postpone postpone next action | |
-s, --search ERE search your todos | |
-V, --version show version | |
ENDHELP | |
} | |
# Similar projects | |
# ------------------------------------------------------- | |
# | |
# Here are some other command line todo managers: | |
# | |
# - [todo.txt](http://ginatrapani.github.com/todo.txt-cli/): | |
# written in Bash, featureful. | |
# - [TaskWarrior](http://taskwarrior.org) | |
# written in C, featureful. The superest CLI todo manager I've encountered. | |
# - [t](http://stevelosh.com/projects/t/) | |
# written in Python, for people that want to finish tasks, not organize them. | |
# - [todo](https://github.com/juanibiapina/todo) | |
# minimalist per directory TODO list manager written in Bash. | |
# Releases | |
# ------------------------------------------------------- | |
readonly semver='0.1.0' # released on 2011-04 | |
# Next action is the earliest created/modified action. | |
# You can postpone next action now. | |
# Default $ftm_repo to ~/.ftm. | |
# `ftm -c [todo]` will echo todo name. | |
# BUGFIX: `ftm -c` and `ftm -e` will not open all todos. | |
# readonly semver='0.0.1' # released on 2011-02 | |
# BUGFIX: Run `ftm` without options will prompot you to type `ftm -h` now. | |
# BUGFIX: Fix wrong help info. | |
# readonly semver='0.0.0' # released on 2011-02 | |
# Usage | |
# ------------------------------------------------------- | |
# | |
# First use `ftm --init dir` to specify the directory to store your todos. | |
# If you omit the `dir` parameter, ftm will use `~/.ftm`. | |
# Note ftm will refuse to work if you use interesting characters | |
# such as space and dot in directory name. | |
readonly ftm_repo=$HOME/.ftm | |
init_ftm() { | |
readonly dir=$1 | |
# Set todo repo path by *modifying source*. | |
# So we don't need an additional config file. | |
set_repo_bang() { | |
readonly var_pattern="^(readonly $1=).*" | |
readonly new_var_pattern=$2 | |
sed -i -r -e "s:$var_pattern:\1$new_var_pattern:" $0 | |
} | |
[ -n "$dir" ] && set_repo_bang 'ftm_repo' $dir | |
# We cannot simply use $ftm_repo here. | |
# Remember set_repo_bang changed the source, *not* the variable. | |
mkdir -p ${dir:-$ftm_repo}/.done | |
} | |
# Add todos: | |
# | |
# ftm -a I've some thing to do | |
# | |
# Ftm will convert whitespace and punctions to '-'. | |
# For example, the previous todo will be stored as `I-ve-some-thing-to-do`. | |
add_todo() { | |
# convert special characters | |
nice_filename() { | |
readonly raw_text="$*" | |
echo $raw_text | tr [:punct:] '-' | tr ' \t' '-' | |
} | |
readonly todo="$*" | |
touch $ftm_repo/$(nice_filename $todo) | |
} | |
# You can check next action with: `ftm -n`. | |
# Reversely sorted by modification time. | |
# It mainly meant for scripting usage. | |
# I find myself almost always use `ftm -c` instead. | |
next_action() { | |
ls -t $ftm_repo | tail -1 | |
} | |
# You can postpone next action, i.e. put it to the tail of the queue. | |
postpone_action() { | |
touch $ftm_repo/$(next_action) | |
} | |
# For every todo, you can add notes to it, look at its notes, | |
# and mark it as done. | |
# | |
# ftm --<action> [todo] | |
# | |
# Available actions: | |
# | |
# * `-e|--edit` | |
# * `-c|--cat` | |
# * `-d|--done` | |
# | |
# You specify todo item by their name, but you can be lazy by | |
# only typing the beginnig part of the name as long as it will produce | |
# a *unique* match. | |
# (If not unique, the behavior is undefined. | |
# In fact, `-c` and `-e` may support edit/view multiple todo items, | |
# but `-d` will certainly fail.) | |
# If you do not provide a todo name, ftm will operate on the next action. | |
act_on_todo() { | |
readonly todo_action=$1 | |
readonly todo=$2 | |
[ $todo ] && $todo_action $ftm_repo/$todo* || $todo_action $ftm_repo/$(next_action) | |
} | |
# For example, add/edit notes todo with your prefered editor. | |
# (The one your `editor` points to.) | |
# | |
# ftm -e I-ve-some-thing-to-do | |
# | |
edit_todo() { | |
readonly to_edit_todo=$1 | |
act_on_todo 'editor' $to_edit_todo | |
} | |
# Look at the note in a lazy way: | |
# | |
# ftm -c I-ve | |
# It simply prints the note to standard output. | |
# I've considered using `less` when it detects the note is very long. | |
# But I dropped this approach for simplicity. | |
# After all, I gusee todo notes tend to be short. | |
# And if you do have a long note, you can always pipe the standard | |
# output to a pager, or scroll back. | |
cat_todo() { | |
show_todo() { | |
readonly to_show_todo=$1 | |
echo $(basename $to_show_todo) | |
echo # print a new line | |
cat $to_show_todo | |
} | |
readonly to_cat_todo=$1 | |
act_on_todo show_todo $to_cat_todo | |
} | |
# When you mark something done, ftm moves it into a subdirectory `.done`, | |
# with the time you marked it done appenden to its name. | |
# For example: | |
# | |
# my-todo -> .done/my-todo/2000-01-01T13-01-01TZ | |
# | |
# You may use other software to generating reports from them. | |
finish_todo() { | |
readonly to_mark_done_todo=$1 | |
mark_done() { | |
readonly done_todo=$1 | |
mv $done_todo $ftm_repo/.done/$(basename $done_todo)_$(date -u +%Y-%m-%dT%H-%M-%STZ) | |
} | |
act_on_todo mark_done $to_mark_done_todo | |
} | |
# `ftm -l` will list all todos. | |
# `ftm -l word` will list any todo containing the word. | |
list_todos() { | |
readonly todo_word=$1 | |
# `$ftm_repo/*$1*` will print full path. Thus we change directory. | |
cd $ftm_repo | |
ls *$todo_word* | |
cd $OLDPWD | |
} | |
# Ftm also supports full text search: | |
# | |
# `ftm -s regexp` | |
# | |
# We uses ERE. Ftm will not search todos marked as done. | |
search_todos() { | |
readonly ERE_pattern="$*" | |
grep --colour -C3 -EHne $ERE_pattern $ftm_repo/* | |
} | |
main() { | |
case $1 in | |
-a|--add) shift 1 && add_todo "$@";; | |
-c|--cat) cat_todo $2;; | |
-e|--edit) edit_todo $2;; | |
-d|--done) finish_todo $2;; | |
-h|--help) help;; | |
--init) init_ftm $2;; | |
-l|--list) list_todos $2;; | |
-n|--next) next_action;; | |
-p|--postpone) postpone_action;; | |
-s|--search) shift 1 && search_todos "$@";; | |
-V|--version) echo ftm version $semver;; | |
*) echo 'Type ftm -h for help.'; exit 1;; | |
esac | |
} | |
main "$@" | |
# Examples | |
# ------------------------------ | |
# | |
# Ftm tries hard to keep things simple. | |
# But due to its 'every todo is a file' design, | |
# you can use it in an 'advanced' way. | |
# | |
# Use number | |
# prefix for priority. | |
# For example: | |
# | |
# - 0-TAKE-OVER-THE-WORLD | |
# - 1-fly-to-the-moon | |
# - 2-sleep | |
# - 21-eat | |
# - 3-who-cares | |
# | |
# And so on. | |
# With 10 numbers, you can build a very sophisticated priority system. | |
# And uses the filter `ftm -l`: | |
# | |
# Check what's urgent? | |
# | |
# ftm -l 0 | |
# | |
# Check all todos with a priority higher than 3: | |
# | |
# ftm -l [0-3] | |
# | |
# Besides priority, it's also possible to build a context system, for example: | |
# | |
# - 0-TAKE-OVER-THE-WORLD-HOME | |
# - 1-fly-to-the-moon-ERRAND | |
# | |
# I will go out: | |
# | |
# ftm -l ERRAND | |
# | |
# I'm at home, what's the next action at home: | |
# | |
# ftm -l HOME | head -1 | |
# | |
# I'm in office, and check the most important tasks today: | |
# | |
# ftm -l OFFICE | grep [0-3] | |
# | |
# There are many other possibilities, be creative! | |
# But always remember, focus on doing, rather than organizing todos | |
# or writing todo managers. ;-) | |
# BUGS | |
# ---- | |
# | |
# - If you mark two identical notes in the same day, the latter will | |
# overwrite the former silently. | |
# Since this is unlikely to happen, I've no plan to fix it yet. | |
# | |
# | |
# Bugs in earlier version: | |
# | |
# <0.1.0: | |
# - Keep todos in ~/.config/ftm is against XDG. | |
# Should use $XDG_DATA_HOME/ftm instead. | |
# (Reported by astolia) | |
# | |
# - Neither `ftm -c` or `ftm -e` works as expected. | |
# It should show/edit the note of next action, but it actually | |
# opens notes of all todos. | |
# | |
# 0.0.0: | |
# - Help says typing `ftm` without options will change directory | |
# to $ftm_repo. | |
# Since it's in a subshell, this changing is nonsense. | |
# - Help gives wrong information on option 'add'. The parametre | |
# is not optional. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment