Make ls-Family Actions Use Listfile Backend
Essentially,
ls)
shift
exec "$TODO_SH" listfile "$TODO_FILE" "$@"
;;
lsa)
shift
cat "$TODO_FILE" "$DONE_FILE" > "$TMP_FILE"
exec $TODO_SH listfile "$TMP_FILE" "$@"
;;
lsp)
shift ## was "listpri"
shift ## was priority
exec $TODO_SH listfile "$TODO_FILE" "$pri" "$@"
;;;
Also adds the following features:
1. Numbers are padded with up to five zeros (but only the minimum
necessary), letting you list up to 999,999 tasks with the same
formatting.
2. All ls-family commands hide context, priority, and project when
the user sets those hide options.
3. Quoted arguments are passed on to grep as whole arguments,
enabling the following:
$ todo.sh ls buy a | head -n2
34 Buy a portable gas can
22 Buy door
$ todo.sh ls "buy a"
34 Buy a portable gas can
4. listfile can take an absolute path. Any filename starting with a
"/" will be treated as an absolute path; any other filename will
be treated as relative to $TODO_DIR. Since a leading "/" would be
striped by the operating system anyway under the old code, this
is fully backward compatible.
Contains the following regressions:
1. The ls verbose line count messages are more generic.
2. There is no verbose line count line for lspri.
3. I don't think listfile's absolute path feature will work on
Windows. If it doesn't, either this patch needs to be thrown
away, listall needs to be rewritten, or (my preference) $TMP_FILE
needs to set as relative to $TODO_DIR.
This commit is contained in:
280
todo.sh
280
todo.sh
@@ -225,7 +225,7 @@ do
|
|||||||
unset HIDE_CONTEXTS_SUBSTITUTION
|
unset HIDE_CONTEXTS_SUBSTITUTION
|
||||||
else
|
else
|
||||||
## One or odd value -- hide context names
|
## One or odd value -- hide context names
|
||||||
HIDE_CONTEXTS_SUBSTITUTION='[[:space:]]@[^[:space:]]\{1,\}'
|
export HIDE_CONTEXTS_SUBSTITUTION='[[:space:]]@[^[:space:]]\{1,\}'
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
'+' )
|
'+' )
|
||||||
@@ -241,7 +241,7 @@ do
|
|||||||
unset HIDE_PROJECTS_SUBSTITUTION
|
unset HIDE_PROJECTS_SUBSTITUTION
|
||||||
else
|
else
|
||||||
## One or odd value -- hide project names
|
## One or odd value -- hide project names
|
||||||
HIDE_PROJECTS_SUBSTITUTION='[[:space:]][+][^[:space:]]\{1,\}'
|
export HIDE_PROJECTS_SUBSTITUTION='[[:space:]][+][^[:space:]]\{1,\}'
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
a )
|
a )
|
||||||
@@ -275,7 +275,7 @@ do
|
|||||||
unset HIDE_PRIORITY_SUBSTITUTION
|
unset HIDE_PRIORITY_SUBSTITUTION
|
||||||
else
|
else
|
||||||
## One or odd value -- hide priority labels
|
## One or odd value -- hide priority labels
|
||||||
HIDE_PRIORITY_SUBSTITUTION="([A-Z])[[:space:]]"
|
export HIDE_PRIORITY_SUBSTITUTION="([A-Z])[[:space:]]"
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
t )
|
t )
|
||||||
@@ -323,7 +323,7 @@ export TODO_SH
|
|||||||
[ -d "$TODO_DIR" ] || die "Fatal Error: $TODO_DIR is not a directory"
|
[ -d "$TODO_DIR" ] || die "Fatal Error: $TODO_DIR is not a directory"
|
||||||
cd "$TODO_DIR" || die "Fatal Error: Unable to cd to $TODO_DIR"
|
cd "$TODO_DIR" || die "Fatal Error: Unable to cd to $TODO_DIR"
|
||||||
|
|
||||||
echo '' > "$TMP_FILE" || die "Fatal Error: Unable to write in $TODO_DIR"
|
[ -w "$TMP_FILE" ] || echo -n > $TMP_FILE || die "Fatal Error: Unable to write to $TMP_FILE"
|
||||||
[ -f "$TODO_FILE" ] || cp /dev/null "$TODO_FILE"
|
[ -f "$TODO_FILE" ] || cp /dev/null "$TODO_FILE"
|
||||||
[ -f "$DONE_FILE" ] || cp /dev/null "$DONE_FILE"
|
[ -f "$DONE_FILE" ] || cp /dev/null "$DONE_FILE"
|
||||||
[ -f "$REPORT_FILE" ] || cp /dev/null "$REPORT_FILE"
|
[ -f "$REPORT_FILE" ] || cp /dev/null "$REPORT_FILE"
|
||||||
@@ -339,61 +339,6 @@ fi
|
|||||||
# === HEAVY LIFTING ===
|
# === HEAVY LIFTING ===
|
||||||
shopt -s extglob
|
shopt -s extglob
|
||||||
|
|
||||||
_list-zero_padding() {
|
|
||||||
local LINES=$( wc -l $FILE_TO_NUMBER | sed 's/ .*//' )
|
|
||||||
|
|
||||||
local i=0
|
|
||||||
for i in $( seq 1 6 )
|
|
||||||
do
|
|
||||||
if [ $(( $LINES / 10 ** $i )) -gt 0 ]
|
|
||||||
then
|
|
||||||
:
|
|
||||||
else
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
echo $i
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
_list-number_lines() {
|
|
||||||
local FILE_TO_NUMBER="$1"
|
|
||||||
local PADDING=$( _list-zero_padding "$FILE_TO_NUMBER" )
|
|
||||||
|
|
||||||
sed = "$FILE_TO_NUMBER" \
|
|
||||||
| sed "N; s/^/ /; s/ *\(.\{$PADDING,\}\)\n/\1 /" \
|
|
||||||
| sed '''
|
|
||||||
s/^ /00000/;
|
|
||||||
s/^ /0000/;
|
|
||||||
s/^ /000/;
|
|
||||||
s/^ /00/;
|
|
||||||
s/^ /0/;
|
|
||||||
'''
|
|
||||||
}
|
|
||||||
|
|
||||||
_list-sort_alphabetically() {
|
|
||||||
sort -f -k2
|
|
||||||
}
|
|
||||||
|
|
||||||
_list-add_priority_color() {
|
|
||||||
local FILE_TO_NUMBER="$1"
|
|
||||||
local PADDING=$( _list-zero_padding "$FILE_TO_NUMBER" )
|
|
||||||
|
|
||||||
sed '/^[0-9]\{'$PADDING'\} x /! {
|
|
||||||
s/\(.*(A).*\)/'$PRI_A'\1 '$DEFAULT'/g;
|
|
||||||
s/\(.*(B).*\)/'$PRI_B'\1 '$DEFAULT'/g;
|
|
||||||
s/\(.*(C).*\)/'$PRI_C'\1 '$DEFAULT'/g;
|
|
||||||
s/\(.*([D-Z]).*\)/'$PRI_X'\1 '$DEFAULT'/g;
|
|
||||||
}'
|
|
||||||
}
|
|
||||||
|
|
||||||
_list-hide_priority_projects_contexts() {
|
|
||||||
sed 's/'${HIDE_PRIORITY_SUBSTITUTION:-^}'//g' \
|
|
||||||
| sed 's/'${HIDE_PROJECTS_SUBSTITUTION:-^}'//g' \
|
|
||||||
| sed 's/'${HIDE_CONTEXTS_SUBSTITUTION:-^}'//g'
|
|
||||||
}
|
|
||||||
|
|
||||||
# == HANDLE ACTION ==
|
# == HANDLE ACTION ==
|
||||||
action=$( printf "%s\n" "$1" | tr 'A-Z' 'a-z' )
|
action=$( printf "%s\n" "$1" | tr 'A-Z' 'a-z' )
|
||||||
|
|
||||||
@@ -560,108 +505,124 @@ case $action in
|
|||||||
|
|
||||||
|
|
||||||
"list" | "ls" )
|
"list" | "ls" )
|
||||||
item=$2
|
|
||||||
if [ -z "$item" ]; then
|
|
||||||
echo -e "$( \
|
|
||||||
_list-number_lines "$TODO_FILE" \
|
|
||||||
| _list-sort_alphabetically \
|
|
||||||
| _list-add_priority_color "$TODO_FILE" \
|
|
||||||
| _list-hide_priority_projects_contexts \
|
|
||||||
)"
|
|
||||||
echo "--"
|
|
||||||
NUMTASKS=$(wc -l "$TODO_FILE" | sed 's/^[[:space:]]*\([0-9]*\).*/\1/')
|
|
||||||
echo "TODO: $NUMTASKS tasks in $TODO_FILE."
|
|
||||||
else
|
|
||||||
command=$(
|
|
||||||
_list-number_lines "$TODO_FILE" \
|
|
||||||
| _list-sort_alphabetically \
|
|
||||||
| _list-add_priority_color "$TODO_FILE" \
|
|
||||||
| grep -i $item
|
|
||||||
)
|
|
||||||
shift
|
shift
|
||||||
shift
|
exec "$TODO_SH" listfile "$TODO_FILE" "$@"
|
||||||
for i in $*
|
;;
|
||||||
do
|
|
||||||
command=`echo "$command" | grep -i $i `
|
|
||||||
done
|
|
||||||
command=$( \
|
|
||||||
echo "$command" \
|
|
||||||
| _list-sort_alphabetically \
|
|
||||||
| _list-hide_priority_projects_contexts \
|
|
||||||
)
|
|
||||||
echo -e "$command"
|
|
||||||
fi
|
|
||||||
cleanup ;;
|
|
||||||
|
|
||||||
"listall" | "lsa" )
|
"listall" | "lsa" )
|
||||||
item=$2
|
|
||||||
cat "$TODO_FILE" "$DONE_FILE" > "$TMP_FILE"
|
cat "$TODO_FILE" "$DONE_FILE" > "$TMP_FILE"
|
||||||
|
|
||||||
if [ -z "$item" ]; then
|
|
||||||
echo -e "$(
|
|
||||||
_list-number_lines "$TMP_FILE" \
|
|
||||||
| _list-sort_alphabetically \
|
|
||||||
| _list-add_priority_color "$TMP_FILE" \
|
|
||||||
)"
|
|
||||||
else
|
|
||||||
command=$(
|
|
||||||
_list-number_lines "$TMP_FILE" \
|
|
||||||
| _list-sort_alphabetically \
|
|
||||||
| _list-add_priority_color "$TMP_FILE" \
|
|
||||||
| grep -i $item
|
|
||||||
)
|
|
||||||
shift
|
shift
|
||||||
shift
|
exec $TODO_SH listfile "$TMP_FILE" "$@"
|
||||||
for i in $*
|
;;
|
||||||
do
|
|
||||||
command=`echo "$command" | grep -i $i `
|
|
||||||
done
|
|
||||||
command=`echo "$command" | sort -f -k2`
|
|
||||||
echo -e "$command"
|
|
||||||
fi
|
|
||||||
cleanup ;;
|
|
||||||
|
|
||||||
|
|
||||||
"listfile" | "lf" )
|
"listfile" | "lf" )
|
||||||
src="$TODO_DIR/$2"
|
## If the file starts with a "/" use absolute path. Otherwise, make
|
||||||
|
## it relative to $TODO_DIR
|
||||||
if [ -z "$3" ]; then
|
if [ "${2:0:1}" == / ]
|
||||||
item=""
|
then
|
||||||
|
## Absolute path
|
||||||
|
src="$2"
|
||||||
else
|
else
|
||||||
item=$3
|
## Relative path
|
||||||
|
src="$TODO_DIR/$2"
|
||||||
fi
|
fi
|
||||||
if [ -f "$src" ]; then
|
|
||||||
|
## Ensure the file exits before proceeding
|
||||||
|
if [ ! -f $src ]
|
||||||
|
then
|
||||||
|
echo "TODO: File $src does not exist."
|
||||||
|
fi
|
||||||
|
|
||||||
|
## Figure out how much padding we need to use
|
||||||
|
## We need one level of padding for each power of 10 $LINES uses
|
||||||
|
LINES=$( wc -l "$src" | sed 's/ .*//' )
|
||||||
|
i=0
|
||||||
|
## 6 places will pad tasks numbered less than 99,999, thus correctly
|
||||||
|
## displaying up to 999,999 tasks. Anyone who's done a million tasks
|
||||||
|
## should send me an email with your secret.
|
||||||
|
for i in $( seq 1 6 )
|
||||||
|
do
|
||||||
|
if [ ! $(( $LINES / 10 ** $i )) -gt 0 ]
|
||||||
|
then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
PADDING=$i
|
||||||
|
unset i LINES
|
||||||
|
|
||||||
|
item="${3:-}"
|
||||||
if [ -z "$item" ]; then
|
if [ -z "$item" ]; then
|
||||||
|
## No search pattern specified, operate on whole file
|
||||||
echo -e "$(
|
echo -e "$(
|
||||||
_list-number_lines "$src" \
|
sed = "$src" \
|
||||||
| _list-sort_alphabetically \
|
| sed "N; s/^/ /; s/ *\(.\{$PADDING,\}\)\n/\1 /" \
|
||||||
| _list-add_priority_color "$src"
|
| sed '''
|
||||||
|
s/^ /00000/;
|
||||||
|
s/^ /0000/;
|
||||||
|
s/^ /000/;
|
||||||
|
s/^ /00/;
|
||||||
|
s/^ /0/;
|
||||||
|
''' \
|
||||||
|
| sort -f -k2 \
|
||||||
|
| sed '''
|
||||||
|
/^[0-9]\{'$PADDING'\} x /! {
|
||||||
|
s/\(.*(A).*\)/'$PRI_A'\1 '$DEFAULT'/g;
|
||||||
|
s/\(.*(B).*\)/'$PRI_B'\1 '$DEFAULT'/g;
|
||||||
|
s/\(.*(C).*\)/'$PRI_C'\1 '$DEFAULT'/g;
|
||||||
|
s/\(.*([D-Z]).*\)/'$PRI_X'\1 '$DEFAULT'/g;
|
||||||
|
}
|
||||||
|
s/'${HIDE_PRIORITY_SUBSTITUTION:-^}'//g
|
||||||
|
s/'${HIDE_PROJECTS_SUBSTITUTION:-^}'//g
|
||||||
|
s/'${HIDE_CONTEXTS_SUBSTITUTION:-^}'//g
|
||||||
|
''' \
|
||||||
)"
|
)"
|
||||||
if [ $TODOTXT_VERBOSE = 1 ]; then
|
if [ $TODOTXT_VERBOSE = 1 ]; then
|
||||||
echo "--"
|
echo "--"
|
||||||
NUMTASKS=$( sed '/./!d' "$src" | wc -l | sed 's/^[[:space:]]*\([0-9]*\).*/\1/')
|
NUMTASKS=$( sed '/./!d' "$src" | wc -l | sed 's/^[[:space:]]*\([0-9]*\).*/\1/')
|
||||||
echo "TODO: $NUMTASKS lines in $src."
|
echo "TODO: $NUMTASKS lines shown from $src"
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
|
## Search pattern or patterns specified; parse the file and then
|
||||||
|
## grep for each pattern afterwords.
|
||||||
command=$(
|
command=$(
|
||||||
_list-number_lines "$src" \
|
sed = "$src" \
|
||||||
| _list-sort_alphabetically \
|
| sed "N; s/^/ /; s/ *\(.\{$PADDING,\}\)\n/\1 /" \
|
||||||
| _list-add_priority_color "$src" \
|
| sed '''
|
||||||
| grep -i $item
|
s/^ /00000/;
|
||||||
|
s/^ /0000/;
|
||||||
|
s/^ /000/;
|
||||||
|
s/^ /00/;
|
||||||
|
s/^ /0/;
|
||||||
|
''' \
|
||||||
|
| sort -f -k2 \
|
||||||
|
| sed '''
|
||||||
|
/^[0-9]\{'$PADDING'\} x /! {
|
||||||
|
s/\(.*(A).*\)/'$PRI_A'\1 '$DEFAULT'/g;
|
||||||
|
s/\(.*(B).*\)/'$PRI_B'\1 '$DEFAULT'/g;
|
||||||
|
s/\(.*(C).*\)/'$PRI_C'\1 '$DEFAULT'/g;
|
||||||
|
s/\(.*([D-Z]).*\)/'$PRI_X'\1 '$DEFAULT'/g;
|
||||||
|
}
|
||||||
|
''' \
|
||||||
|
| grep -i "$item"
|
||||||
)
|
)
|
||||||
shift
|
shift ## was "listfile"
|
||||||
shift
|
shift ## was first search pattern
|
||||||
for i in $*
|
for i in "$@"
|
||||||
do
|
do
|
||||||
command=`echo "$command" | grep -i $i `
|
command=`echo "$command" | grep -i "$i" `
|
||||||
done
|
done
|
||||||
command=`echo "$command" | sort -f -k2`
|
command=$( echo "$command" \
|
||||||
|
| sort -f -k2 \
|
||||||
|
| sed '''
|
||||||
|
s/'${HIDE_PRIORITY_SUBSTITUTION:-^}'//g
|
||||||
|
s/'${HIDE_PROJECTS_SUBSTITUTION:-^}'//g
|
||||||
|
s/'${HIDE_CONTEXTS_SUBSTITUTION:-^}'//g
|
||||||
|
''' \
|
||||||
|
)
|
||||||
echo -e "$command"
|
echo -e "$command"
|
||||||
fi
|
fi
|
||||||
else
|
cleanup
|
||||||
echo "TODO: File $src does not exist."
|
;;
|
||||||
fi
|
|
||||||
cleanup ;;
|
|
||||||
|
|
||||||
"listcon" | "lsc" )
|
"listcon" | "lsc" )
|
||||||
gawk '{for(i = 1; i <= NF; i++) print $i}' "$TODO_FILE" | grep '@' | sort | uniq
|
gawk '{for(i = 1; i <= NF; i++) print $i}' "$TODO_FILE" | grep '@' | sort | uniq
|
||||||
@@ -673,38 +634,21 @@ case $action in
|
|||||||
|
|
||||||
|
|
||||||
"listpri" | "lsp" )
|
"listpri" | "lsp" )
|
||||||
pri=$( printf "%s\n" "$2" | tr 'a-z' 'A-Z' )
|
if [ "${2:-}" ]
|
||||||
errmsg="usage: $0 listpri PRIORITY
|
then
|
||||||
note: PRIORITY must a single letter from A to Z."
|
pri=$( printf "%s\n" "$2" | tr 'a-z' 'A-Z' | grep '^[A-Z]$' ) || {
|
||||||
|
die "usage: $0 listpri PRIORITY
|
||||||
if [ -z "$pri" ]; then
|
note: PRIORITY must a single letter from A to Z."
|
||||||
echo -e "$(
|
}
|
||||||
_list-number_lines "$TODO_FILE" \
|
|
||||||
| _list-sort_alphabetically \
|
|
||||||
| _list-add_priority_color "$TODO_FILE" \
|
|
||||||
| grep \([A-Z]\)
|
|
||||||
)"
|
|
||||||
if [ $TODOTXT_VERBOSE = 1 ]; then
|
|
||||||
echo "--"
|
|
||||||
NUMTASKS=$(grep \([A-Z]\) "$TODO_FILE" | wc -l | sed 's/^[[:space:]]*\([0-9]*\).*/\1/')
|
|
||||||
echo "TODO: $NUMTASKS prioritized tasks in $TODO_FILE."
|
|
||||||
fi
|
|
||||||
else
|
else
|
||||||
[[ "$pri" = +([A-Z]) ]] || die "$errmsg"
|
pri="[A-Z]"
|
||||||
|
fi
|
||||||
|
pri="($pri)"
|
||||||
|
|
||||||
echo -e "$(
|
shift ## was "listpri"
|
||||||
_list-number_lines "$TODO_FILE" \
|
shift ## was priority
|
||||||
| _list-sort_alphabetically \
|
exec $TODO_SH listfile "$TODO_FILE" "$pri" "$@"
|
||||||
| _list-add_priority_color "$TODO_FILE" \
|
;;
|
||||||
| grep \($pri\)
|
|
||||||
)"
|
|
||||||
if [ $TODOTXT_VERBOSE = 1 ]; then
|
|
||||||
echo "--"
|
|
||||||
NUMTASKS=$(grep \($pri\) "$TODO_FILE" | wc -l | sed 's/^[[:space:]]*\([0-9]*\).*/\1/')
|
|
||||||
echo "TODO: $NUMTASKS tasks prioritized $pri in $TODO_FILE."
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
cleanup;;
|
|
||||||
|
|
||||||
"move" | "mv" )
|
"move" | "mv" )
|
||||||
# replace moved line with a blank line when TODOTXT_PRESERVE_LINE_NUMBERS is 1
|
# replace moved line with a blank line when TODOTXT_PRESERVE_LINE_NUMBERS is 1
|
||||||
|
|||||||
Reference in New Issue
Block a user