Compare commits

...

7 Commits

Author SHA1 Message Date
Florian Tham
19dde076cf remove accidentally commited swap file 2018-11-06 11:08:01 +01:00
Florian Tham
3bc3616651 optionally colorized date extensions (due, threshold and again) 2018-11-06 10:58:18 +01:00
kh80
d589fd0570 Add 'done' option as alias of 'do' (#169) 2018-04-11 13:34:19 -05:00
tpikonen
7a30199fe0 Cleanup (#247)
- Fixed: Reduce shellcheck warnings.
- Fixed: Remove trailing whitespace.
- Fixed: Double quote to prevent globbing and word splitting. [SC2086]
- Fixed: Invalid flags are not handled. [SC2220]
- Fixed: Cleaned up string replaces.
2018-04-04 19:33:09 -05:00
tpikonen
867ade5701 new: Add default priority variable TODOTXT_PRIORITY_ON_ADD. (#246)
Adds the config / env variable `TODOTXT_PRIORITY_ON_ADD`, which when set to one of the capital letters `A` to `Z` is given as a priority to a new task, if a priority is not given on command line.
2018-03-28 16:58:48 -05:00
Anton Ermolenko
14f5de1579 interpreter reference update to be portable (#160) 2018-03-28 16:09:07 -05:00
Guilherme Reis
7ec2a1263e Read the task's name and uses as default (#249)
Read the task's name and uses as default, so the user can read the previous task's name.

Thanks @guifeliper
2018-03-26 13:23:27 -05:00
3 changed files with 158 additions and 96 deletions

33
tests/t1040-add-priority.sh Executable file
View File

@@ -0,0 +1,33 @@
#!/bin/bash
test_description='test the priority on add feature'
. ./test-lib.sh
## Normal use case
echo "export TODOTXT_PRIORITY_ON_ADD=A" >> todo.cfg
test_todo_session 'config file priority' <<EOF
>>> todo.sh add take out the trash
1 (A) take out the trash
TODO: 1 added.
>>> todo.sh -p list
1 (A) take out the trash
--
TODO: 1 of 1 tasks shown
EOF
## Wrong value in config var
echo "export TODOTXT_PRIORITY_ON_ADD=1" >> todo.cfg
test_todo_session 'config file wrong priority' <<EOF
>>> todo.sh add fail to take out the trash
=== 1
TODOTXT_PRIORITY_ON_ADD should be a capital letter from A to Z (it is now "1").
>>> todo.sh -p list
=== 1
TODOTXT_PRIORITY_ON_ADD should be a capital letter from A to Z (it is now "1").
EOF
test_done

View File

@@ -67,6 +67,10 @@ export REPORT_FILE="$TODO_DIR/report.txt"
# export COLOR_PROJECT=$RED # export COLOR_PROJECT=$RED
# export COLOR_CONTEXT=$RED # export COLOR_CONTEXT=$RED
# ... and for date extensions (due:yyyy-mm-dd, t:yyyy-mm-dd and again:X)
#
# export COLOR_DATEEXT=$LIGHT_BLUE
# === BEHAVIOR === # === BEHAVIOR ===
## customize list output ## customize list output

209
todo.sh
View File

@@ -1,4 +1,4 @@
#! /bin/bash #!/usr/bin/env bash
# === HEAVY LIFTING === # === HEAVY LIFTING ===
shopt -s extglob extquote shopt -s extglob extquote
@@ -51,7 +51,7 @@ shorthelp()
deduplicate deduplicate
del|rm ITEM# [TERM] del|rm ITEM# [TERM]
depri|dp ITEM#[, ITEM#, ITEM#, ...] depri|dp ITEM#[, ITEM#, ITEM#, ...]
do ITEM#[, ITEM#, ITEM#, ...] done|do ITEM#[, ITEM#, ITEM#, ...]
help [ACTION...] help [ACTION...]
list|ls [TERM...] list|ls [TERM...]
listall|lsa [TERM...] listall|lsa [TERM...]
@@ -134,7 +134,7 @@ help()
EndOptionsHelp EndOptionsHelp
[ $TODOTXT_VERBOSE -gt 1 ] && cat <<-'EndVerboseHelp' [ "$TODOTXT_VERBOSE" -gt 1 ] && cat <<-'EndVerboseHelp'
Environment variables: Environment variables:
TODOTXT_AUTO_ARCHIVE is same as option -a (0)/-A (1) TODOTXT_AUTO_ARCHIVE is same as option -a (0)/-A (1)
TODOTXT_CFG_FILE=CONFIG_FILE is same as option -d CONFIG_FILE TODOTXT_CFG_FILE=CONFIG_FILE is same as option -d CONFIG_FILE
@@ -142,6 +142,7 @@ help()
TODOTXT_PRESERVE_LINE_NUMBERS is same as option -n (0)/-N (1) TODOTXT_PRESERVE_LINE_NUMBERS is same as option -n (0)/-N (1)
TODOTXT_PLAIN is same as option -p (1)/-c (0) TODOTXT_PLAIN is same as option -p (1)/-c (0)
TODOTXT_DATE_ON_ADD is same as option -t (1)/-T (0) TODOTXT_DATE_ON_ADD is same as option -t (1)/-T (0)
TODOTXT_PRIORITY_ON_ADD=pri default priority A-Z
TODOTXT_VERBOSE=1 is same as option -v TODOTXT_VERBOSE=1 is same as option -v
TODOTXT_DISABLE_FILTER=1 is same as option -x TODOTXT_DISABLE_FILTER=1 is same as option -x
TODOTXT_DEFAULT_ACTION="" run this when called with no arguments TODOTXT_DEFAULT_ACTION="" run this when called with no arguments
@@ -200,6 +201,7 @@ actionsHelp()
Deprioritizes (removes the priority) from the task(s) Deprioritizes (removes the priority) from the task(s)
on line ITEM# in todo.txt. on line ITEM# in todo.txt.
done ITEM#[, ITEM#, ITEM#, ...]
do ITEM#[, ITEM#, ITEM#, ...] do ITEM#[, ITEM#, ITEM#, ...]
Marks task(s) on line ITEM# as done in todo.txt. Marks task(s) on line ITEM# as done in todo.txt.
@@ -292,7 +294,7 @@ addonHelp()
didPrintAddonActionsHeader= didPrintAddonActionsHeader=
for action in "$TODO_ACTIONS_DIR"/* for action in "$TODO_ACTIONS_DIR"/*
do do
if [ -f "$action" -a -x "$action" ]; then if [ -f "$action" ] && [ -x "$action" ]; then
if [ ! "$didPrintAddonActionsHeader" ]; then if [ ! "$didPrintAddonActionsHeader" ]; then
cat <<-EndAddonActionsHeader cat <<-EndAddonActionsHeader
Add-on Actions: Add-on Actions:
@@ -300,14 +302,14 @@ addonHelp()
didPrintAddonActionsHeader=1 didPrintAddonActionsHeader=1
fi fi
"$action" usage "$action" usage
elif [ -d "$action" -a -x "$action/$(basename $action)" ]; then elif [ -d "$action" ] && [ -x "$action"/"$(basename "$action")" ]; then
if [ ! "$didPrintAddonActionsHeader" ]; then if [ ! "$didPrintAddonActionsHeader" ]; then
cat <<-EndAddonActionsHeader cat <<-EndAddonActionsHeader
Add-on Actions: Add-on Actions:
EndAddonActionsHeader EndAddonActionsHeader
didPrintAddonActionsHeader=1 didPrintAddonActionsHeader=1
fi fi
"$action/$(basename $action)" usage "$action"/"$(basename "$action")" usage
fi fi
done done
fi fi
@@ -318,10 +320,10 @@ actionUsage()
for actionName for actionName
do do
action="${TODO_ACTIONS_DIR}/${actionName}" action="${TODO_ACTIONS_DIR}/${actionName}"
if [ -f "$action" -a -x "$action" ]; then if [ -f "$action" ] && [ -x "$action" ]; then
"$action" usage "$action" usage
elif [ -d "$action" -a -x "$action/$(basename $action)" ]; then elif [ -d "$action" ] && [ -x "$action"/"$(basename "$action")" ]; then
"$action/$(basename $action)" usage "$action"/"$(basename "$action")" usage
else else
builtinActionUsage=$(actionsHelp | sed -n -e "/^ ${actionName//\//\\/} /,/^\$/p" -e "/^ ${actionName//\//\\/}$/,/^\$/p") builtinActionUsage=$(actionsHelp | sed -n -e "/^ ${actionName//\//\\/} /,/^\$/p" -e "/^ ${actionName//\//\\/}$/,/^\$/p")
if [ "$builtinActionUsage" ]; then if [ "$builtinActionUsage" ]; then
@@ -376,8 +378,9 @@ getPrefix()
# Parameters: $1: todo file; empty means $TODO_FILE. # Parameters: $1: todo file; empty means $TODO_FILE.
# Returns: Uppercase FILE prefix to be used in place of "TODO:" where # Returns: Uppercase FILE prefix to be used in place of "TODO:" where
# a different todo file can be specified. # a different todo file can be specified.
local base=$(basename "${1:-$TODO_FILE}") local base
echo "${base%%.[^.]*}" | tr 'a-z' 'A-Z' base=$(basename "${1:-$TODO_FILE}")
echo "${base%%.[^.]*}" | tr '[:lower:]' '[:upper:]'
} }
getTodo() getTodo()
@@ -402,8 +405,8 @@ getNewtodo()
# Postcondition: $newtodo contains task text. # Postcondition: $newtodo contains task text.
local item=$1 local item=$1
[ -z "$item" ] && die 'Programming error: $item should exist.' [ -z "$item" ] && die "Programming error: $item should exist."
[ "${item//[0-9]/}" ] && die 'Programming error: $item should be numeric.' [ "${item//[0-9]/}" ] && die "Programming error: $item should be numeric."
newtodo=$(sed "$item!d" "${2:-$TODO_FILE}") newtodo=$(sed "$item!d" "${2:-$TODO_FILE}")
[ -z "$newtodo" ] && die "$(getPrefix "$2"): No updated task $item." [ -z "$newtodo" ] && die "$(getPrefix "$2"): No updated task $item."
@@ -427,7 +430,7 @@ replaceOrPrepend()
if [[ -z "$1" && $TODOTXT_FORCE = 0 ]]; then if [[ -z "$1" && $TODOTXT_FORCE = 0 ]]; then
echo -n "$querytext" echo -n "$querytext"
read -e -r input read -r -i "$todo" -e input
else else
input=$* input=$*
fi fi
@@ -437,7 +440,7 @@ replaceOrPrepend()
priority=$(sed -e "$item!d" -e "${item}s/${priAndDateExpr}.*/\\1/" "$TODO_FILE") priority=$(sed -e "$item!d" -e "${item}s/${priAndDateExpr}.*/\\1/" "$TODO_FILE")
prepdate=$(sed -e "$item!d" -e "${item}s/${priAndDateExpr}.*/\\2/" "$TODO_FILE") prepdate=$(sed -e "$item!d" -e "${item}s/${priAndDateExpr}.*/\\2/" "$TODO_FILE")
if [ "$prepdate" -a "$action" = "replace" ] && [ "$(echo "$input"|sed -e "s/${priAndDateExpr}.*/\\1\\2/")" ]; then if [ "$prepdate" ] && [ "$action" = "replace" ] && [ "$(echo "$input"|sed -e "s/${priAndDateExpr}.*/\\1\\2/")" ]; then
# If the replaced text starts with a [priority +] date, it will replace # If the replaced text starts with a [priority +] date, it will replace
# the existing date, too. # the existing date, too.
prepdate= prepdate=
@@ -448,7 +451,7 @@ replaceOrPrepend()
# date again. # date again.
cleaninput "for sed" cleaninput "for sed"
sed -i.bak -e "$item s/^${priority}${prepdate}//" -e "$item s|^.*|${priority}${prepdate}${input}${backref}|" "$TODO_FILE" sed -i.bak -e "$item s/^${priority}${prepdate}//" -e "$item s|^.*|${priority}${prepdate}${input}${backref}|" "$TODO_FILE"
if [ $TODOTXT_VERBOSE -gt 0 ]; then if [ "$TODOTXT_VERBOSE" -gt 0 ]; then
getNewtodo "$item" getNewtodo "$item"
case "$action" in case "$action" in
replace) replace)
@@ -473,7 +476,7 @@ uppercasePriority()
do do
upperPriority="${upperPriority};s/^[(]${lower[i]}[)]/(${upper[i]})/" upperPriority="${upperPriority};s/^[(]${lower[i]}[)]/(${upper[i]})/"
done done
input=$(echo "$input" | sed $upperPriority) input=$(echo "$input" | sed "$upperPriority")
} }
#Preserving environment variables so they don't get clobbered by the config file #Preserving environment variables so they don't get clobbered by the config file
@@ -482,6 +485,7 @@ OVR_TODOTXT_FORCE="$TODOTXT_FORCE"
OVR_TODOTXT_PRESERVE_LINE_NUMBERS="$TODOTXT_PRESERVE_LINE_NUMBERS" OVR_TODOTXT_PRESERVE_LINE_NUMBERS="$TODOTXT_PRESERVE_LINE_NUMBERS"
OVR_TODOTXT_PLAIN="$TODOTXT_PLAIN" OVR_TODOTXT_PLAIN="$TODOTXT_PLAIN"
OVR_TODOTXT_DATE_ON_ADD="$TODOTXT_DATE_ON_ADD" OVR_TODOTXT_DATE_ON_ADD="$TODOTXT_DATE_ON_ADD"
OVR_TODOTXT_PRIORITY_ON_ADD="$TODOTXT_PRIORITY_ON_ADD"
OVR_TODOTXT_DISABLE_FILTER="$TODOTXT_DISABLE_FILTER" OVR_TODOTXT_DISABLE_FILTER="$TODOTXT_DISABLE_FILTER"
OVR_TODOTXT_VERBOSE="$TODOTXT_VERBOSE" OVR_TODOTXT_VERBOSE="$TODOTXT_VERBOSE"
OVR_TODOTXT_DEFAULT_ACTION="$TODOTXT_DEFAULT_ACTION" OVR_TODOTXT_DEFAULT_ACTION="$TODOTXT_DEFAULT_ACTION"
@@ -489,20 +493,20 @@ OVR_TODOTXT_SORT_COMMAND="$TODOTXT_SORT_COMMAND"
OVR_TODOTXT_FINAL_FILTER="$TODOTXT_FINAL_FILTER" OVR_TODOTXT_FINAL_FILTER="$TODOTXT_FINAL_FILTER"
# Prevent GREP_OPTIONS from malforming grep's output # Prevent GREP_OPTIONS from malforming grep's output
GREP_OPTIONS="" export GREP_OPTIONS=""
# == PROCESS OPTIONS == # == PROCESS OPTIONS ==
while getopts ":fhpcnNaAtTvVx+@Pd:" Option while getopts ":fhpcnNaAtTvVx+@Pd:" Option
do do
case $Option in case $Option in
'@' ) '@')
## HIDE_CONTEXT_NAMES starts at zero (false); increment it to one ## HIDE_CONTEXT_NAMES starts at zero (false); increment it to one
## (true) the first time this flag is seen. Each time the flag ## (true) the first time this flag is seen. Each time the flag
## is seen after that, increment it again so that an even ## is seen after that, increment it again so that an even
## number shows context names and an odd number hides context ## number shows context names and an odd number hides context
## names. ## names.
: $(( HIDE_CONTEXT_NAMES++ )) : $(( HIDE_CONTEXT_NAMES++ ))
if [ $(( $HIDE_CONTEXT_NAMES % 2 )) -eq 0 ] if [ $(( HIDE_CONTEXT_NAMES % 2 )) -eq 0 ]
then then
## Zero or even value -- show context names ## Zero or even value -- show context names
unset HIDE_CONTEXTS_SUBSTITUTION unset HIDE_CONTEXTS_SUBSTITUTION
@@ -511,14 +515,14 @@ do
export HIDE_CONTEXTS_SUBSTITUTION='[[:space:]]@[[:graph:]]\{1,\}' export HIDE_CONTEXTS_SUBSTITUTION='[[:space:]]@[[:graph:]]\{1,\}'
fi fi
;; ;;
'+' ) '+')
## HIDE_PROJECT_NAMES starts at zero (false); increment it to one ## HIDE_PROJECT_NAMES starts at zero (false); increment it to one
## (true) the first time this flag is seen. Each time the flag ## (true) the first time this flag is seen. Each time the flag
## is seen after that, increment it again so that an even ## is seen after that, increment it again so that an even
## number shows project names and an odd number hides project ## number shows project names and an odd number hides project
## names. ## names.
: $(( HIDE_PROJECT_NAMES++ )) : $(( HIDE_PROJECT_NAMES++ ))
if [ $(( $HIDE_PROJECT_NAMES % 2 )) -eq 0 ] if [ $(( HIDE_PROJECT_NAMES % 2 )) -eq 0 ]
then then
## Zero or even value -- show project names ## Zero or even value -- show project names
unset HIDE_PROJECTS_SUBSTITUTION unset HIDE_PROJECTS_SUBSTITUTION
@@ -527,45 +531,45 @@ do
export HIDE_PROJECTS_SUBSTITUTION='[[:space:]][+][[:graph:]]\{1,\}' export HIDE_PROJECTS_SUBSTITUTION='[[:space:]][+][[:graph:]]\{1,\}'
fi fi
;; ;;
a ) a)
OVR_TODOTXT_AUTO_ARCHIVE=0 OVR_TODOTXT_AUTO_ARCHIVE=0
;; ;;
A ) A)
OVR_TODOTXT_AUTO_ARCHIVE=1 OVR_TODOTXT_AUTO_ARCHIVE=1
;; ;;
c ) c)
OVR_TODOTXT_PLAIN=0 OVR_TODOTXT_PLAIN=0
;; ;;
d ) d)
TODOTXT_CFG_FILE=$OPTARG TODOTXT_CFG_FILE=$OPTARG
;; ;;
f ) f)
OVR_TODOTXT_FORCE=1 OVR_TODOTXT_FORCE=1
;; ;;
h ) h)
# Short-circuit option parsing and forward to the action. # Short-circuit option parsing and forward to the action.
# Cannot just invoke shorthelp() because we need the configuration # Cannot just invoke shorthelp() because we need the configuration
# processed to locate the add-on actions directory. # processed to locate the add-on actions directory.
set -- '-h' 'shorthelp' set -- '-h' 'shorthelp'
OPTIND=2 OPTIND=2
;; ;;
n ) n)
OVR_TODOTXT_PRESERVE_LINE_NUMBERS=0 OVR_TODOTXT_PRESERVE_LINE_NUMBERS=0
;; ;;
N ) N)
OVR_TODOTXT_PRESERVE_LINE_NUMBERS=1 OVR_TODOTXT_PRESERVE_LINE_NUMBERS=1
;; ;;
p ) p)
OVR_TODOTXT_PLAIN=1 OVR_TODOTXT_PLAIN=1
;; ;;
P ) P)
## HIDE_PRIORITY_LABELS starts at zero (false); increment it to one ## HIDE_PRIORITY_LABELS starts at zero (false); increment it to one
## (true) the first time this flag is seen. Each time the flag ## (true) the first time this flag is seen. Each time the flag
## is seen after that, increment it again so that an even ## is seen after that, increment it again so that an even
## number shows priority labels and an odd number hides priority ## number shows priority labels and an odd number hides priority
## labels. ## labels.
: $(( HIDE_PRIORITY_LABELS++ )) : $(( HIDE_PRIORITY_LABELS++ ))
if [ $(( $HIDE_PRIORITY_LABELS % 2 )) -eq 0 ] if [ $(( HIDE_PRIORITY_LABELS % 2 )) -eq 0 ]
then then
## Zero or even value -- show priority labels ## Zero or even value -- show priority labels
unset HIDE_PRIORITY_SUBSTITUTION unset HIDE_PRIORITY_SUBSTITUTION
@@ -574,24 +578,27 @@ do
export HIDE_PRIORITY_SUBSTITUTION="([A-Z])[[:space:]]" export HIDE_PRIORITY_SUBSTITUTION="([A-Z])[[:space:]]"
fi fi
;; ;;
t ) t)
OVR_TODOTXT_DATE_ON_ADD=1 OVR_TODOTXT_DATE_ON_ADD=1
;; ;;
T ) T)
OVR_TODOTXT_DATE_ON_ADD=0 OVR_TODOTXT_DATE_ON_ADD=0
;; ;;
v ) v)
: $(( TODOTXT_VERBOSE++ )) : $(( TODOTXT_VERBOSE++ ))
;; ;;
V ) V)
version version
;; ;;
x ) x)
OVR_TODOTXT_DISABLE_FILTER=1 OVR_TODOTXT_DISABLE_FILTER=1
;; ;;
*)
usage
;;
esac esac
done done
shift $(($OPTIND - 1)) shift $((OPTIND - 1))
# defaults if not yet defined # defaults if not yet defined
TODOTXT_VERBOSE=${TODOTXT_VERBOSE:-1} TODOTXT_VERBOSE=${TODOTXT_VERBOSE:-1}
@@ -601,6 +608,7 @@ TODOTXT_FORCE=${TODOTXT_FORCE:-0}
TODOTXT_PRESERVE_LINE_NUMBERS=${TODOTXT_PRESERVE_LINE_NUMBERS:-1} TODOTXT_PRESERVE_LINE_NUMBERS=${TODOTXT_PRESERVE_LINE_NUMBERS:-1}
TODOTXT_AUTO_ARCHIVE=${TODOTXT_AUTO_ARCHIVE:-1} TODOTXT_AUTO_ARCHIVE=${TODOTXT_AUTO_ARCHIVE:-1}
TODOTXT_DATE_ON_ADD=${TODOTXT_DATE_ON_ADD:-0} TODOTXT_DATE_ON_ADD=${TODOTXT_DATE_ON_ADD:-0}
TODOTXT_PRIORITY_ON_ADD=${TODOTXT_PRIORITY_ON_ADD:-}
TODOTXT_DEFAULT_ACTION=${TODOTXT_DEFAULT_ACTION:-} TODOTXT_DEFAULT_ACTION=${TODOTXT_DEFAULT_ACTION:-}
TODOTXT_SORT_COMMAND=${TODOTXT_SORT_COMMAND:-env LC_COLLATE=C sort -f -k2} TODOTXT_SORT_COMMAND=${TODOTXT_SORT_COMMAND:-env LC_COLLATE=C sort -f -k2}
TODOTXT_DISABLE_FILTER=${TODOTXT_DISABLE_FILTER:-} TODOTXT_DISABLE_FILTER=${TODOTXT_DISABLE_FILTER:-}
@@ -608,7 +616,7 @@ TODOTXT_FINAL_FILTER=${TODOTXT_FINAL_FILTER:-cat}
TODOTXT_GLOBAL_CFG_FILE=${TODOTXT_GLOBAL_CFG_FILE:-/etc/todo/config} TODOTXT_GLOBAL_CFG_FILE=${TODOTXT_GLOBAL_CFG_FILE:-/etc/todo/config}
# Export all TODOTXT_* variables # Export all TODOTXT_* variables
export ${!TODOTXT_@} export "${!TODOTXT_@}"
# Default color map # Default color map
export NONE='' export NONE=''
@@ -636,9 +644,10 @@ export PRI_B=$GREEN # color for B priority
export PRI_C=$LIGHT_BLUE # color for C priority export PRI_C=$LIGHT_BLUE # color for C priority
export PRI_X=$WHITE # color unless explicitly defined export PRI_X=$WHITE # color unless explicitly defined
# Default project and context colors. # Default project, context and dateext colors.
export COLOR_PROJECT=$NONE export COLOR_PROJECT=$NONE
export COLOR_CONTEXT=$NONE export COLOR_CONTEXT=$NONE
export COLOR_DATEEXT=$NONE
# Default highlight colors. # Default highlight colors.
export COLOR_DONE=$LIGHT_GREY # color for done (but not yet archived) tasks export COLOR_DONE=$LIGHT_GREY # color for done (but not yet archived) tasks
@@ -695,7 +704,7 @@ export SENTENCE_DELIMITERS=',.:;'
} }
if [ -z "$TODO_ACTIONS_DIR" -o ! -d "$TODO_ACTIONS_DIR" ] if [ -z "$TODO_ACTIONS_DIR" ] || [ ! -d "$TODO_ACTIONS_DIR" ]
then then
TODO_ACTIONS_DIR="$HOME/.todo/actions" TODO_ACTIONS_DIR="$HOME/.todo/actions"
export TODO_ACTIONS_DIR export TODO_ACTIONS_DIR
@@ -740,6 +749,9 @@ fi
if [ -n "$OVR_TODOTXT_DATE_ON_ADD" ] ; then if [ -n "$OVR_TODOTXT_DATE_ON_ADD" ] ; then
TODOTXT_DATE_ON_ADD="$OVR_TODOTXT_DATE_ON_ADD" TODOTXT_DATE_ON_ADD="$OVR_TODOTXT_DATE_ON_ADD"
fi fi
if [ -n "$OVR_TODOTXT_PRIORITY_ON_ADD" ] ; then
TODOTXT_PRIORITY_ON_ADD="$OVR_TODOTXT_PRIORITY_ON_ADD"
fi
if [ -n "$OVR_TODOTXT_DISABLE_FILTER" ] ; then if [ -n "$OVR_TODOTXT_DISABLE_FILTER" ] ; then
TODOTXT_DISABLE_FILTER="$OVR_TODOTXT_DISABLE_FILTER" TODOTXT_DISABLE_FILTER="$OVR_TODOTXT_DISABLE_FILTER"
fi fi
@@ -759,16 +771,19 @@ fi
ACTION=${1:-$TODOTXT_DEFAULT_ACTION} ACTION=${1:-$TODOTXT_DEFAULT_ACTION}
[ -z "$ACTION" ] && usage [ -z "$ACTION" ] && usage
[ -d "$TODO_DIR" ] || mkdir -p $TODO_DIR 2> /dev/null || dieWithHelp "$1" "Fatal Error: $TODO_DIR is not a directory" [ -d "$TODO_DIR" ] || mkdir -p "$TODO_DIR" 2> /dev/null || dieWithHelp "$1" "Fatal Error: $TODO_DIR is not a directory"
( cd "$TODO_DIR" ) || dieWithHelp "$1" "Fatal Error: Unable to cd to $TODO_DIR" ( cd "$TODO_DIR" ) || dieWithHelp "$1" "Fatal Error: Unable to cd to $TODO_DIR"
[ -z "$TODOTXT_PRIORITY_ON_ADD" ] \
|| echo "$TODOTXT_PRIORITY_ON_ADD" | grep -q "^[A-Z]$" \
|| die "TODOTXT_PRIORITY_ON_ADD should be a capital letter from A to Z (it is now \"$TODOTXT_PRIORITY_ON_ADD\")."
[ -f "$TODO_FILE" -o -c "$TODO_FILE" ] || > "$TODO_FILE" [ -f "$TODO_FILE" ] || [ -c "$TODO_FILE" ] || > "$TODO_FILE"
[ -f "$DONE_FILE" -o -c "$DONE_FILE" ] || > "$DONE_FILE" [ -f "$DONE_FILE" ] || [ -c "$DONE_FILE" ] || > "$DONE_FILE"
[ -f "$REPORT_FILE" -o -c "$REPORT_FILE" ] || > "$REPORT_FILE" [ -f "$REPORT_FILE" ] || [ -c "$REPORT_FILE" ] || > "$REPORT_FILE"
if [ $TODOTXT_PLAIN = 1 ]; then if [ $TODOTXT_PLAIN = 1 ]; then
for clr in ${!PRI_@}; do for clr in ${!PRI_@}; do
export $clr=$NONE export "$clr"=$NONE
done done
PRI_X=$NONE PRI_X=$NONE
DEFAULT=$NONE DEFAULT=$NONE
@@ -786,12 +801,17 @@ _addto() {
cleaninput cleaninput
uppercasePriority uppercasePriority
if [[ $TODOTXT_DATE_ON_ADD = 1 ]]; then if [[ "$TODOTXT_DATE_ON_ADD" -eq 1 ]]; then
now=$(date '+%Y-%m-%d') now=$(date '+%Y-%m-%d')
input=$(echo "$input" | sed -e 's/^\(([A-Z]) \)\{0,1\}/\1'"$now /") input=$(echo "$input" | sed -e 's/^\(([A-Z]) \)\{0,1\}/\1'"$now /")
fi fi
if [[ -n "$TODOTXT_PRIORITY_ON_ADD" ]]; then
if ! echo "$input" | grep -q '^([A-Z])'; then
input=$(echo -n "($TODOTXT_PRIORITY_ON_ADD) " ; echo "$input")
fi
fi
echo "$input" >> "$file" echo "$input" >> "$file"
if [ $TODOTXT_VERBOSE -gt 0 ]; then if [ "$TODOTXT_VERBOSE" -gt 0 ]; then
TASKNUM=$(sed -n '$ =' "$file") TASKNUM=$(sed -n '$ =' "$file")
echo "$TASKNUM $input" echo "$TASKNUM $input"
echo "$(getPrefix "$file"): $TASKNUM added." echo "$(getPrefix "$file"): $TASKNUM added."
@@ -859,7 +879,7 @@ _list() {
_format "$src" '' "$@" _format "$src" '' "$@"
if [ $TODOTXT_VERBOSE -gt 0 ]; then if [ "$TODOTXT_VERBOSE" -gt 0 ]; then
echo "--" echo "--"
echo "$(getPrefix "$src"): ${NUMTASKS:-0} of ${TOTALTASKS:-0} tasks shown" echo "$(getPrefix "$src"): ${NUMTASKS:-0} of ${TOTALTASKS:-0} tasks shown"
fi fi
@@ -919,7 +939,7 @@ _format()
s/^ /00/; s/^ /00/;
s/^ /0/; s/^ /0/;
''' \ ''' \
| eval ${TODOTXT_SORT_COMMAND} \ | eval "${TODOTXT_SORT_COMMAND}" \
| awk ''' | awk '''
function highlight(colorVar, color) { function highlight(colorVar, color) {
color = ENVIRON[colorVar] color = ENVIRON[colorVar]
@@ -945,6 +965,9 @@ _format()
ctx_beg = highlight("COLOR_CONTEXT") ctx_beg = highlight("COLOR_CONTEXT")
ctx_end = (ctx_beg ? (highlight("DEFAULT") clr) : "") ctx_end = (ctx_beg ? (highlight("DEFAULT") clr) : "")
dtx_beg = highlight("COLOR_DATEEXT")
dtx_end = (dtx_beg ? (highlight("DEFAULT") clr) : "")
gsub(/[ \t][ \t]*/, "\n&\n") gsub(/[ \t][ \t]*/, "\n&\n")
len = split($0, words, /\n/) len = split($0, words, /\n/)
@@ -954,7 +977,9 @@ _format()
printf "%s", prj_beg words[i] prj_end printf "%s", prj_beg words[i] prj_end
} else if (words[i] ~ /^[@].*[A-Za-z0-9_]$/) { } else if (words[i] ~ /^[@].*[A-Za-z0-9_]$/) {
printf "%s", ctx_beg words[i] ctx_end printf "%s", ctx_beg words[i] ctx_end
} else { } else if (words[i] ~ /^(due|t|again):/) {
printf "%s", dtx_beg words[i] dtx_end
} else {
printf "%s", words[i] printf "%s", words[i]
} }
} }
@@ -970,11 +995,11 @@ _format()
) )
[ "$filtered_items" ] && echo "$filtered_items" [ "$filtered_items" ] && echo "$filtered_items"
if [ $TODOTXT_VERBOSE -gt 0 ]; then if [ "$TODOTXT_VERBOSE" -gt 0 ]; then
NUMTASKS=$( echo -n "$filtered_items" | sed -n '$ =' ) NUMTASKS=$( echo -n "$filtered_items" | sed -n '$ =' )
TOTALTASKS=$( echo -n "$items" | sed -n '$ =' ) TOTALTASKS=$( echo -n "$items" | sed -n '$ =' )
fi fi
if [ $TODOTXT_VERBOSE -gt 1 ]; then if [ "$TODOTXT_VERBOSE" -gt 1 ]; then
echo "TODO DEBUG: Filter Command was: ${filter_command:-cat}" echo "TODO DEBUG: Filter Command was: ${filter_command:-cat}"
fi fi
} }
@@ -992,7 +1017,7 @@ listWordsWithSigil()
export -f cleaninput getPrefix getTodo getNewtodo shellquote filtercommand _list listWordsWithSigil getPadding _format die export -f cleaninput getPrefix getTodo getNewtodo shellquote filtercommand _list listWordsWithSigil getPadding _format die
# == HANDLE ACTION == # == HANDLE ACTION ==
action=$( printf "%s\n" "$ACTION" | tr 'A-Z' 'a-z' ) action=$( printf "%s\n" "$ACTION" | tr '[:upper:]' '[:lower:]' )
## If the first argument is "command", run the rest of the arguments ## If the first argument is "command", run the rest of the arguments
## using todo.sh builtins. ## using todo.sh builtins.
@@ -1003,12 +1028,12 @@ then
## Get rid of "command" from arguments list ## Get rid of "command" from arguments list
shift shift
## Reset action to new first argument ## Reset action to new first argument
action=$( printf "%s\n" "$1" | tr 'A-Z' 'a-z' ) action=$( printf "%s\n" "$1" | tr '[:upper:]' '[:lower:]' )
elif [ -d "$TODO_ACTIONS_DIR/$action" -a -x "$TODO_ACTIONS_DIR/$action/$action" ] elif [ -d "$TODO_ACTIONS_DIR/$action" ] && [ -x "$TODO_ACTIONS_DIR/$action/$action" ]
then then
"$TODO_ACTIONS_DIR/$action/$action" "$@" "$TODO_ACTIONS_DIR/$action/$action" "$@"
exit $? exit $?
elif [ -d "$TODO_ACTIONS_DIR" -a -x "$TODO_ACTIONS_DIR/$action" ] elif [ -d "$TODO_ACTIONS_DIR" ] && [ -x "$TODO_ACTIONS_DIR/$action" ]
then then
"$TODO_ACTIONS_DIR/$action" "$@" "$TODO_ACTIONS_DIR/$action" "$@"
exit $? exit $?
@@ -1082,8 +1107,8 @@ case $action in
esac esac
cleaninput "for sed" cleaninput "for sed"
if sed -i.bak $item" s|^.*|&${appendspace}${input}|" "$TODO_FILE"; then if sed -i.bak "${item} s|^.*|&${appendspace}${input}|" "$TODO_FILE"; then
if [ $TODOTXT_VERBOSE -gt 0 ]; then if [ "$TODOTXT_VERBOSE" -gt 0 ]; then
getNewtodo "$item" getNewtodo "$item"
echo "$item $newtodo" echo "$item $newtodo"
fi fi
@@ -1095,10 +1120,10 @@ case $action in
"archive" ) "archive" )
# defragment blank lines # defragment blank lines
sed -i.bak -e '/./!d' "$TODO_FILE" sed -i.bak -e '/./!d' "$TODO_FILE"
[ $TODOTXT_VERBOSE -gt 0 ] && grep "^x " "$TODO_FILE" [ "$TODOTXT_VERBOSE" -gt 0 ] && grep "^x " "$TODO_FILE"
grep "^x " "$TODO_FILE" >> "$DONE_FILE" grep "^x " "$TODO_FILE" >> "$DONE_FILE"
sed -i.bak '/^x /d' "$TODO_FILE" sed -i.bak '/^x /d' "$TODO_FILE"
if [ $TODOTXT_VERBOSE -gt 0 ]; then if [ "$TODOTXT_VERBOSE" -gt 0 ]; then
echo "TODO: $TODO_FILE archived." echo "TODO: $TODO_FILE archived."
fi fi
;; ;;
@@ -1119,12 +1144,12 @@ case $action in
if [ "$ANSWER" = "y" ]; then if [ "$ANSWER" = "y" ]; then
if [ $TODOTXT_PRESERVE_LINE_NUMBERS = 0 ]; then if [ $TODOTXT_PRESERVE_LINE_NUMBERS = 0 ]; then
# delete line (changes line numbers) # delete line (changes line numbers)
sed -i.bak -e $item"s/^.*//" -e '/./!d' "$TODO_FILE" sed -i.bak -e "${item}s/^.*//" -e '/./!d' "$TODO_FILE"
else else
# leave blank line behind (preserves line numbers) # leave blank line behind (preserves line numbers)
sed -i.bak -e $item"s/^.*//" "$TODO_FILE" sed -i.bak -e "${item}s/^.*//" "$TODO_FILE"
fi fi
if [ $TODOTXT_VERBOSE -gt 0 ]; then if [ "$TODOTXT_VERBOSE" -gt 0 ]; then
echo "$item $todo" echo "$item $todo"
echo "TODO: $item deleted." echo "TODO: $item deleted."
fi fi
@@ -1133,18 +1158,18 @@ case $action in
fi fi
else else
sed -i.bak \ sed -i.bak \
-e $item"s/^\((.) \)\{0,1\} *$3 */\1/g" \ -e "${item}s/^\((.) \)\{0,1\} *$3 */\1/g" \
-e $item"s/ *$3 *\$//g" \ -e "${item}s/ *$3 *\$//g" \
-e $item"s/ *$3 */ /g" \ -e "${item}s/ *$3 */ /g" \
-e $item"s/ *$3 */ /g" \ -e "${item}s/ *$3 */ /g" \
-e $item"s/$3//g" \ -e "${item}s/$3//g" \
"$TODO_FILE" "$TODO_FILE"
getNewtodo "$item" getNewtodo "$item"
if [ "$todo" = "$newtodo" ]; then if [ "$todo" = "$newtodo" ]; then
[ $TODOTXT_VERBOSE -gt 0 ] && echo "$item $todo" [ "$TODOTXT_VERBOSE" -gt 0 ] && echo "$item $todo"
die "TODO: '$3' not found; no removal done." die "TODO: '$3' not found; no removal done."
fi fi
if [ $TODOTXT_VERBOSE -gt 0 ]; then if [ "$TODOTXT_VERBOSE" -gt 0 ]; then
echo "$item $todo" echo "$item $todo"
echo "TODO: Removed '$3' from task." echo "TODO: Removed '$3' from task."
echo "$item $newtodo" echo "$item $newtodo"
@@ -1163,8 +1188,8 @@ case $action in
getTodo "$item" getTodo "$item"
if [[ "$todo" = \(?\)\ * ]]; then if [[ "$todo" = \(?\)\ * ]]; then
sed -i.bak -e $item"s/^(.) //" "$TODO_FILE" sed -i.bak -e "${item}s/^(.) //" "$TODO_FILE"
if [ $TODOTXT_VERBOSE -gt 0 ]; then if [ "$TODOTXT_VERBOSE" -gt 0 ]; then
getNewtodo "$item" getNewtodo "$item"
echo "$item $newtodo" echo "$item $newtodo"
echo "TODO: $item deprioritized." echo "TODO: $item deprioritized."
@@ -1175,7 +1200,7 @@ case $action in
done done
;; ;;
"do" ) "do" | "done" )
errmsg="usage: $TODO_SH do ITEM#[, ITEM#, ITEM#, ...]" errmsg="usage: $TODO_SH do ITEM#[, ITEM#, ITEM#, ...]"
# shift so we get arguments to the do request # shift so we get arguments to the do request
shift; shift;
@@ -1190,9 +1215,9 @@ case $action in
if [ "${todo:0:2}" != "x " ]; then if [ "${todo:0:2}" != "x " ]; then
now=$(date '+%Y-%m-%d') now=$(date '+%Y-%m-%d')
# remove priority once item is done # remove priority once item is done
sed -i.bak $item"s/^(.) //" "$TODO_FILE" sed -i.bak "${item}s/^(.) //" "$TODO_FILE"
sed -i.bak $item"s|^|x $now |" "$TODO_FILE" sed -i.bak "${item}s|^|x $now |" "$TODO_FILE"
if [ $TODOTXT_VERBOSE -gt 0 ]; then if [ "$TODOTXT_VERBOSE" -gt 0 ]; then
getNewtodo "$item" getNewtodo "$item"
echo "$item $newtodo" echo "$item $newtodo"
echo "TODO: $item marked as done." echo "TODO: $item marked as done."
@@ -1249,7 +1274,7 @@ case $action in
post_filter_command="${post_filter_command:-}${post_filter_command:+ | }awk -v TOTAL=$TOTAL -v PADDING=$PADDING '{ \$1 = sprintf(\"%\" PADDING \"d\", (\$1 > TOTAL ? 0 : \$1)); print }' " post_filter_command="${post_filter_command:-}${post_filter_command:+ | }awk -v TOTAL=$TOTAL -v PADDING=$PADDING '{ \$1 = sprintf(\"%\" PADDING \"d\", (\$1 > TOTAL ? 0 : \$1)); print }' "
cat "$TODO_FILE" "$DONE_FILE" | TODOTXT_VERBOSE=0 _format '' "$PADDING" "$@" cat "$TODO_FILE" "$DONE_FILE" | TODOTXT_VERBOSE=0 _format '' "$PADDING" "$@"
if [ $TODOTXT_VERBOSE -gt 0 ]; then if [ "$TODOTXT_VERBOSE" -gt 0 ]; then
TDONE=$( sed -n '$ =' "$DONE_FILE" ) TDONE=$( sed -n '$ =' "$DONE_FILE" )
TASKNUM=$(TODOTXT_PLAIN=1 TODOTXT_VERBOSE=0 _format "$TODO_FILE" 1 "$@" | sed -n '$ =') TASKNUM=$(TODOTXT_PLAIN=1 TODOTXT_VERBOSE=0 _format "$TODO_FILE" 1 "$@" | sed -n '$ =')
DONENUM=$(TODOTXT_PLAIN=1 TODOTXT_VERBOSE=0 _format "$DONE_FILE" 1 "$@" | sed -n '$ =') DONENUM=$(TODOTXT_PLAIN=1 TODOTXT_VERBOSE=0 _format "$DONE_FILE" 1 "$@" | sed -n '$ =')
@@ -1263,8 +1288,8 @@ case $action in
"listfile" | "lf" ) "listfile" | "lf" )
shift ## Was listfile, next $1 is file name shift ## Was listfile, next $1 is file name
if [ $# -eq 0 ]; then if [ $# -eq 0 ]; then
[ $TODOTXT_VERBOSE -gt 0 ] && echo "Files in the todo.txt directory:" [ "$TODOTXT_VERBOSE" -gt 0 ] && echo "Files in the todo.txt directory:"
cd "$TODO_DIR" && ls -1 *.txt cd "$TODO_DIR" && ls -1 -- *.txt
else else
FILE="$1" FILE="$1"
shift ## Was filename; next $1 is first search term shift ## Was filename; next $1 is first search term
@@ -1286,7 +1311,7 @@ case $action in
"listpri" | "lsp" ) "listpri" | "lsp" )
shift ## was "listpri", new $1 is priority to list or first TERM shift ## was "listpri", new $1 is priority to list or first TERM
pri=$(printf "%s\n" "$1" | tr 'a-z' 'A-Z' | grep -e '^[A-Z]$' -e '^[A-Z]-[A-Z]$') && shift || pri="A-Z" pri=$(printf "%s\n" "$1" | tr '[:lower:]' '[:upper:]' | grep -e '^[A-Z]$' -e '^[A-Z]-[A-Z]$') && shift || pri="A-Z"
post_filter_command="${post_filter_command:-}${post_filter_command:+ | }grep '^ *[0-9]\+ ([${pri}]) '" post_filter_command="${post_filter_command:-}${post_filter_command:+ | }grep '^ *[0-9]\+ ([${pri}]) '"
_list "$TODO_FILE" "$@" _list "$TODO_FILE" "$@"
;; ;;
@@ -1315,14 +1340,14 @@ case $action in
if [ "$ANSWER" = "y" ]; then if [ "$ANSWER" = "y" ]; then
if [ $TODOTXT_PRESERVE_LINE_NUMBERS = 0 ]; then if [ $TODOTXT_PRESERVE_LINE_NUMBERS = 0 ]; then
# delete line (changes line numbers) # delete line (changes line numbers)
sed -i.bak -e $item"s/^.*//" -e '/./!d' "$src" sed -i.bak -e "${item}s/^.*//" -e '/./!d' "$src"
else else
# leave blank line behind (preserves line numbers) # leave blank line behind (preserves line numbers)
sed -i.bak -e $item"s/^.*//" "$src" sed -i.bak -e "${item}s/^.*//" "$src"
fi fi
echo "$todo" >> "$dest" echo "$todo" >> "$dest"
if [ $TODOTXT_VERBOSE -gt 0 ]; then if [ "$TODOTXT_VERBOSE" -gt 0 ]; then
echo "$item $todo" echo "$item $todo"
echo "TODO: $item moved from '$src' to '$dest'." echo "TODO: $item moved from '$src' to '$dest'."
fi fi
@@ -1338,7 +1363,7 @@ case $action in
"pri" | "p" ) "pri" | "p" )
item=$2 item=$2
newpri=$( printf "%s\n" "$3" | tr 'a-z' 'A-Z' ) newpri=$( printf "%s\n" "$3" | tr '[:lower:]' '[:upper:]' )
errmsg="usage: $TODO_SH pri ITEM# PRIORITY errmsg="usage: $TODO_SH pri ITEM# PRIORITY
note: PRIORITY must be anywhere from A to Z." note: PRIORITY must be anywhere from A to Z."
@@ -1353,9 +1378,9 @@ note: PRIORITY must be anywhere from A to Z."
fi fi
if [ "$oldpri" != "$newpri" ]; then if [ "$oldpri" != "$newpri" ]; then
sed -i.bak -e $item"s/^(.) //" -e $item"s/^/($newpri) /" "$TODO_FILE" sed -i.bak -e "${item}s/^(.) //" -e "${item}s/^/($newpri) /" "$TODO_FILE"
fi fi
if [ $TODOTXT_VERBOSE -gt 0 ]; then if [ "$TODOTXT_VERBOSE" -gt 0 ]; then
getNewtodo "$item" getNewtodo "$item"
echo "$item $newtodo" echo "$item $newtodo"
if [ "$oldpri" != "$newpri" ]; then if [ "$oldpri" != "$newpri" ]; then
@@ -1389,12 +1414,12 @@ note: PRIORITY must be anywhere from A to Z."
LASTDATA=${LASTREPORT#* } # Strip timestamp. LASTDATA=${LASTREPORT#* } # Strip timestamp.
if [ "$LASTDATA" = "$NEWDATA" ]; then if [ "$LASTDATA" = "$NEWDATA" ]; then
echo "$LASTREPORT" echo "$LASTREPORT"
[ $TODOTXT_VERBOSE -gt 0 ] && echo "TODO: Report file is up-to-date." [ "$TODOTXT_VERBOSE" -gt 0 ] && echo "TODO: Report file is up-to-date."
else else
NEWREPORT="$(date +%Y-%m-%dT%T) ${NEWDATA}" NEWREPORT="$(date +%Y-%m-%dT%T) ${NEWDATA}"
echo "${NEWREPORT}" >> "$REPORT_FILE" echo "${NEWREPORT}" >> "$REPORT_FILE"
echo "${NEWREPORT}" echo "${NEWREPORT}"
[ $TODOTXT_VERBOSE -gt 0 ] && echo "TODO: Report file updated." [ "$TODOTXT_VERBOSE" -gt 0 ] && echo "TODO: Report file updated."
fi fi
;; ;;
@@ -1447,9 +1472,9 @@ note: PRIORITY must be anywhere from A to Z."
cd "$TODO_ACTIONS_DIR" || exit $? cd "$TODO_ACTIONS_DIR" || exit $?
for action in * for action in *
do do
if [ -f "$action" -a -x "$action" ]; then if [ -f "$action" ] && [ -x "$action" ]; then
echo "$action" echo "$action"
elif [ -d "$action" -a -x "$action/$action" ]; then elif [ -d "$action" ] && [ -x "$action/$action" ]; then
echo "$action" echo "$action"
fi fi
done done