Compare commits

..

8 Commits

Author SHA1 Message Date
Ingo Karkat
f4145348d0 Add test for custom add-on action completion. 2012-02-28 10:27:28 +01:00
Ingo Karkat
c9a158338b Add test for todo file completion. 2012-02-28 10:26:59 +01:00
Ingo Karkat
6132bdeb47 Add more test cases for arguments and options completion. 2012-02-28 09:50:00 +01:00
Ingo Karkat
213f6afa38 Add test for task number completion. 2012-02-28 09:43:51 +01:00
Ingo Karkat
7db3a2de13 test-lib: Allow completions containing whitespace.
This is necessary to test the completion of task text.
2012-02-28 09:38:57 +01:00
Ingo Karkat
4f504b26c9 test-lib: Also document test_expect_code. 2012-02-28 09:16:19 +01:00
Ingo Karkat
6c950429f6 ENH: completion can use different todo.sh locations and configurations.
This was previously not supported; todo_completion always used plain todo.sh from the PATH. Now it's possible to override the used executable (and configuration used through _todo_sh, set from a completion function wrapper.

Cp. http://tech.groups.yahoo.com/group/todotxt/message/4003
2012-02-27 14:19:11 +01:00
Ingo Karkat
9fb52d04e6 test-lib: Add test_todo_custom_completion variant.
This extension of test_todo_completion additionally takes the completion function to use, so that customized completions for todo.sh aliases can be tested.
2012-02-27 13:38:58 +01:00
19 changed files with 91 additions and 564 deletions

3
.gitignore vendored
View File

@@ -1,6 +1,3 @@
VERSION-FILE VERSION-FILE
tests/test-results tests/test-results
tests/trash\ directory.* tests/trash\ directory.*
done.txt
report.txt
todo.txt

View File

@@ -1,18 +0,0 @@
#!/bin/bash
make_action()
{
unset TODO_ACTIONS_DIR
[ -d .todo.actions.d ] || mkdir .todo.actions.d
cat > ".todo.actions.d/$1" <<EOF
#!/bin/bash
[ "\$1" = "usage" ] && {
echo " $1 ITEM#[, ITEM#, ...] [TERM...]"
echo " This custom action does $1."
echo ""
exit
}
echo "custom action $1"
EOF
chmod +x ".todo.actions.d/$1"
}

View File

@@ -9,15 +9,8 @@ On failure, it will print each offending line number and line.
' '
. ./test-lib.sh . ./test-lib.sh
backtick_check()
{
sed -n -e 's/\(^\|[ \t]\)#.*//' -e '/`/{' -e '=;p' -e '}' "$@"
}
test_todo_session 'no old-style backtick command substitution' <<EOF test_todo_session 'no old-style backtick command substitution' <<EOF
>>> backtick_check bin/todo.sh >>> sed -n -e 's/\(^\|[ \t]\)#.*//' -e '/\`/{' -e '=;p' -e '}' "$(which todo.sh)"
>>> backtick_check ../../todo.cfg
EOF EOF
test_done test_done

View File

@@ -164,12 +164,4 @@ TODO: Replaced task with:
1 (A) 2010-07-04 this also has a new date 1 (A) 2010-07-04 this also has a new date
EOF EOF
echo '2009-02-13 this is just a new one' > todo.txt
test_todo_session 'replace with prepended priority and date replaces existing date' <<EOF
>>> todo.sh replace 1 '(B) 2010-07-04 this also has a new date'
1 2009-02-13 this is just a new one
TODO: Replaced task with:
1 (B) 2010-07-04 this also has a new date
EOF
test_done test_done

View File

@@ -55,16 +55,6 @@ test_todo_session 'listcon e-mail address test' <<EOF
@con02 @con02
EOF EOF
cat > todo.txt <<EOF
(B) smell the uppercase Roses +roses @outside +shared
(C) notice the sunflowers +sunflowers @garden +shared +landscape
stop
EOF
test_todo_session 'listcon with project' <<EOF
>>> todo.sh listcon +landscape
@garden
EOF
cat > todo.txt <<EOF cat > todo.txt <<EOF
@con01 -- Some context 1 task @con01 -- Some context 1 task
EOF EOF

View File

@@ -1,44 +0,0 @@
#!/bin/bash
#
test_description='help functionality
This test covers the help output.
'
. ./actions-test-lib.sh
. ./test-lib.sh
# Note: To avoid having to adapt the test whenever the help documentation
# slightly changes, only check for the section headers.
test_todo_session 'help output' <<EOF
>>> todo.sh help | sed '/^ [A-Z]/!d'
Usage: todo.sh [-fhpantvV] [-d todo_config] action [task_number] [task_description]
Options:
Built-in Actions:
EOF
test_todo_session 'verbose help output' <<EOF
>>> todo.sh -v help | sed '/^ [A-Z]/!d'
Usage: todo.sh [-fhpantvV] [-d todo_config] action [task_number] [task_description]
Options:
Built-in Actions:
EOF
test_todo_session 'very verbose help output' <<EOF
>>> todo.sh -vv help | sed '/^ [A-Z]/!d'
Usage: todo.sh [-fhpantvV] [-d todo_config] action [task_number] [task_description]
Options:
Environment variables:
Built-in Actions:
EOF
make_action "foo"
test_todo_session 'help output with custom action' <<EOF
>>> todo.sh -v help | sed '/^ [A-Z]/!d'
Usage: todo.sh [-fhpantvV] [-d todo_config] action [task_number] [task_description]
Options:
Built-in Actions:
Add-on Actions:
EOF
test_done

View File

@@ -1,66 +0,0 @@
#!/bin/bash
test_description='built-in actions help functionality
This test checks listing the usage help of a built-in action.
'
. ./test-lib.sh
test_todo_session 'nonexisting action help' <<'EOF'
>>> todo.sh help doesnotexist
TODO: No action "doesnotexist" exists.
=== 1
>>> todo.sh help hel
TODO: No action "hel" exists.
=== 1
>>> todo.sh help h
TODO: No action "h" exists.
=== 1
EOF
test_todo_session 'single action help' <<'EOF'
>>> todo.sh help shorthelp
shorthelp
List the one-line usage of all built-in and add-on actions.
\
EOF
test_todo_session 'multiple actions help' <<'EOF'
>>> todo.sh help shorthelp append
shorthelp
List the one-line usage of all built-in and add-on actions.
\
append ITEM# "TEXT TO APPEND"
app ITEM# "TEXT TO APPEND"
Adds TEXT TO APPEND to the end of the task on line ITEM#.
Quotes optional.
\
EOF
test_todo_session 'short and long form of action help' <<'EOF'
>>> todo.sh help append
append ITEM# "TEXT TO APPEND"
app ITEM# "TEXT TO APPEND"
Adds TEXT TO APPEND to the end of the task on line ITEM#.
Quotes optional.
\
>>> todo.sh help app
app ITEM# "TEXT TO APPEND"
Adds TEXT TO APPEND to the end of the task on line ITEM#.
Quotes optional.
\
EOF
test_todo_session 'mixed existing and nonexisting action help' <<'EOF'
>>> todo.sh help shorthelp doesnotexist list
shorthelp
List the one-line usage of all built-in and add-on actions.
\
TODO: No action "doesnotexist" exists.
=== 1
EOF
test_done

View File

@@ -1,79 +0,0 @@
#!/bin/bash
#
test_description='shorthelp functionality
This test covers the output of the -h option and the shorthelp action.
'
. ./actions-test-lib.sh
. ./test-lib.sh
# Note: To avoid having to adapt the test whenever the actions change, only
# check for the section headers.
test_todo_session '-h output' <<EOF
>>> todo.sh -h | sed '/^ [A-Z]/!d'
Usage: todo.sh [-fhpantvV] [-d todo_config] action [task_number] [task_description]
Actions:
Actions can be added and overridden using scripts in the actions
See "help" for more details.
EOF
test_todo_session 'shorthelp output' <<EOF
>>> todo.sh shorthelp | sed '/^ [A-Z]/!d'
Usage: todo.sh [-fhpantvV] [-d todo_config] action [task_number] [task_description]
Actions:
Actions can be added and overridden using scripts in the actions
See "help" for more details.
EOF
make_action "foo"
test_todo_session 'shorthelp output with custom action' <<EOF
>>> todo.sh -v shorthelp | sed '/^ [A-Z]/!d'
Usage: todo.sh [-fhpantvV] [-d todo_config] action [task_number] [task_description]
Actions:
Actions can be added and overridden using scripts in the actions
Add-on Actions:
See "help" for more details.
EOF
# Verify that custom configuration is actually processed (when the -d option
# precedes the -h option) by specifying a different actions directory and moving
# our custom action there. The help output should mention the "Add-On Actions".
set -o pipefail # So that the sed filter doesn't swallow todo.sh's exit code.
mv todo.cfg custom.cfg
mv .todo.actions.d custom.actions
echo 'export TODO_ACTIONS_DIR=$HOME/custom.actions' >> custom.cfg
test_todo_session '-h and fatal error without config' <<EOF
>>> todo.sh -h | sed '/^ \\{0,2\\}[A-Z]/!d'
Usage: todo.sh [-fhpantvV] [-d todo_config] action [task_number] [task_description]
Actions:
Actions can be added and overridden using scripts in the actions
See "help" for more details.
Fatal Error: Cannot read configuration file $HOME/.todo/config
=== 1
EOF
# Config option comes too late; "Add-on Actions" is *not* mentioned here.
test_todo_session '-h and fatal error with trailing custom config' <<EOF
>>> todo.sh -h -d custom.cfg | sed '/^ \\{0,2\\}[A-Z]/!d'
Usage: todo.sh [-fhpantvV] [-d todo_config] action [task_number] [task_description]
Actions:
Actions can be added and overridden using scripts in the actions
See "help" for more details.
Fatal Error: Cannot read configuration file $HOME/.todo/config
=== 1
EOF
# Config option processed; "Add-on Actions" is mentioned here.
test_todo_session '-h output with preceding custom config' <<EOF
>>> todo.sh -d custom.cfg -h | sed '/^ \\{0,2\\}[A-Z]/!d'
Usage: todo.sh [-fhpantvV] [-d todo_config] action [task_number] [task_description]
Actions:
Actions can be added and overridden using scripts in the actions
Add-on Actions:
See "help" for more details.
EOF
test_done

View File

@@ -7,7 +7,7 @@ This test checks basic todo_completion of actions and options
' '
. ./test-lib.sh . ./test-lib.sh
readonly ACTIONS='add a addto addm append app archive command del rm depri dp do help list ls listaddons listall lsa listcon lsc listfile lf listpri lsp listproj lsprj move mv prepend prep pri p replace report shorthelp' readonly ACTIONS='add a addto addm append app archive command del rm depri dp do help list ls listall lsa listcon lsc listfile lf listpri lsp listproj lsprj move mv prepend prep pri p replace report shorthelp'
readonly OPTIONS='-@ -@@ -+ -++ -d -f -h -p -P -PP -a -n -t -v -vv -V -x' readonly OPTIONS='-@ -@@ -+ -++ -d -f -h -p -P -PP -a -n -t -v -vv -V -x'
test_todo_completion 'all arguments' 'todo.sh ' "$ACTIONS $OPTIONS" test_todo_completion 'all arguments' 'todo.sh ' "$ACTIONS $OPTIONS"

View File

@@ -14,7 +14,6 @@ notice the sunflowers +sunflowers @outside @garden +shared +landscape
(C) 2012-02-28 @outside mow the lawn (C) 2012-02-28 @outside mow the lawn
x 2012-02-21 +herbs @oriental buy spices x 2012-02-21 +herbs @oriental buy spices
x 2012-02-28 2012-02-21 +slack @home watch tv x 2012-02-28 2012-02-21 +slack @home watch tv
2012-02-28 +herbs buy cinnamon @grocer
EOF EOF
test_todo_completion 'simple task' 'todo.sh list 1' '"1 # simple task"' test_todo_completion 'simple task' 'todo.sh list 1' '"1 # simple task"'
test_todo_completion 'remove projects and contents from task' 'todo.sh list 2' '"2 # notice the sunflowers"' test_todo_completion 'remove projects and contents from task' 'todo.sh list 2' '"2 # notice the sunflowers"'
@@ -22,6 +21,5 @@ test_todo_completion 'keep priority' 'todo.sh list 3' '"3 # (B) smell the flower
test_todo_completion 'keep priority and remove timestamp' 'todo.sh list 4' '"4 # (C) mow the lawn"' test_todo_completion 'keep priority and remove timestamp' 'todo.sh list 4' '"4 # (C) mow the lawn"'
test_todo_completion 'keep done marker and remove done date' 'todo.sh list 5' '"5 # x buy spices"' test_todo_completion 'keep done marker and remove done date' 'todo.sh list 5' '"5 # x buy spices"'
test_todo_completion 'keep done marker and remove timestamp and done date' 'todo.sh list 6' '"6 # x watch tv"' test_todo_completion 'keep done marker and remove timestamp and done date' 'todo.sh list 6' '"6 # x watch tv"'
test_todo_completion 'remove add date' 'todo.sh list 7' '"7 # buy cinnamon"'
test_done test_done

View File

@@ -7,68 +7,20 @@ This test checks todo_completion of custom actions in .todo.actions.d
' '
. ./test-lib.sh . ./test-lib.sh
readonly ACTIONS='add a addto addm append app archive command del rm depri dp do help list ls listaddons listall lsa listcon lsc listfile lf listpri lsp listproj lsprj move mv prepend prep pri p replace report shorthelp' readonly ACTIONS='add a addto addm append app archive command del rm depri dp do help list ls listall lsa listcon lsc listfile lf listpri lsp listproj lsprj move mv prepend prep pri p replace report shorthelp'
readonly OPTIONS='-@ -@@ -+ -++ -d -f -h -p -P -PP -a -n -t -v -vv -V -x' readonly OPTIONS='-@ -@@ -+ -++ -d -f -h -p -P -PP -a -n -t -v -vv -V -x'
readonly ADDONS='bar baz foobar' readonly ADDONS='bar baz foobar'
makeCustomActions() mkdir "$HOME/.todo.actions.d"
{ for addon in $ADDONS
set -e do
mkdir "${1:?}" > "$HOME/.todo.actions.d/$addon"
for addon in $ADDONS chmod +x "$HOME/.todo.actions.d/$addon"
do done
addonFile="${1}/$addon"
> "$addonFile"
chmod +x "$addonFile"
done
# Also create a subdirectory, to test that it is skipped.
mkdir "${1}/subdir"
# Also create a non-executable file, to test that it is skipped.
datafile="${1:?}/datafile"
> "$datafile"
chmod -x "$datafile"
[ -x "$datafile" ] && rm "$datafile" # Some file systems may always make files executable; then, skip this check.
set +e
}
removeCustomActions()
{
set -e
rmdir "${1}/subdir"
rm "${1:?}/"*
rmdir "$1"
set +e
}
#
# Test resolution of the default TODO_ACTIONS_DIR.
#
makeCustomActions "$HOME/.todo.actions.d"
test_todo_completion 'all arguments' 'todo.sh ' "$ACTIONS $ADDONS $OPTIONS" test_todo_completion 'all arguments' 'todo.sh ' "$ACTIONS $ADDONS $OPTIONS"
test_todo_completion 'all arguments after option' 'todo.sh -a ' "$ACTIONS $ADDONS $OPTIONS" test_todo_completion 'all arguments after option' 'todo.sh -a ' "$ACTIONS $ADDONS $OPTIONS"
test_todo_completion 'all arguments beginning with b' 'todo.sh b' 'bar baz' test_todo_completion 'all arguments beginning with b' 'todo.sh b' 'bar baz'
test_todo_completion 'all arguments beginning with f after options' 'todo.sh -a -v f' 'foobar' test_todo_completion 'all arguments beginning with f after options' 'todo.sh -a -v f' 'foobar'
test_todo_completion 'nothing after addon action' 'todo.sh foobar ' '' test_todo_completion 'nothing after addon action' 'todo.sh foobar ' ''
removeCustomActions "$HOME/.todo.actions.d"
#
# Test resolution of an alternative TODO_ACTIONS_DIR.
#
mkdir "$HOME/.todo"
makeCustomActions "$HOME/.todo/actions"
test_todo_completion 'all arguments with actions from .todo/actions/' 'todo.sh ' "$ACTIONS $ADDONS $OPTIONS"
removeCustomActions "$HOME/.todo/actions"
#
# Test resolution of a configured TODO_ACTIONS_DIR.
#
makeCustomActions "$HOME/addons"
cat >> todo.cfg <<'EOF'
export TODO_ACTIONS_DIR="$HOME/addons"
EOF
test_todo_completion 'all arguments with actions from addons/' 'todo.sh ' "$ACTIONS $ADDONS $OPTIONS"
removeCustomActions "$HOME/addons"
test_done test_done

View File

@@ -1,20 +0,0 @@
#!/bin/bash
#
test_description='Bash help completion functionality
This test checks todo_completion of actions for usage help.
'
. ./actions-test-lib.sh
. ./test-lib.sh
make_action "zany"
make_action "aardvark"
readonly ACTIONS='add a addto addm append app archive command del rm depri dp do help list ls listaddons listall lsa listcon lsc listfile lf listpri lsp listproj lsprj move mv prepend prep pri p replace report shorthelp'
readonly ADDONS='aardvark zany'
test_todo_completion 'all actions after help' 'todo.sh help ' "$ACTIONS $ADDONS"
test_todo_completion 'all actions after command help' 'todo.sh command help ' "$ACTIONS $ADDONS"
test_todo_completion 'actions beginning with a' 'todo.sh help a' 'add a addto addm append app archive aardvark'
test_done

View File

@@ -4,9 +4,19 @@ test_description='custom actions functionality
This test covers the contract between todo.sh and custom actions. This test covers the contract between todo.sh and custom actions.
' '
. ./actions-test-lib.sh
. ./test-lib.sh . ./test-lib.sh
unset TODO_ACTIONS_DIR
mkdir .todo.actions.d
make_action()
{
cat > ".todo.actions.d/$1" <<- EOF
#!/bin/bash
echo "custom action $1"
EOF
chmod +x ".todo.actions.d/$1"
}
make_action "foo" make_action "foo"
test_todo_session 'executable action' <<EOF test_todo_session 'executable action' <<EOF
>>> todo.sh foo >>> todo.sh foo

View File

@@ -1,44 +0,0 @@
#!/bin/bash
test_description='listaddons functionality
This test checks listing of custom actions.
'
. ./actions-test-lib.sh
. ./test-lib.sh
test_todo_session 'no custom actions' <<EOF
>>> todo.sh listaddons
EOF
make_action "foo"
test_todo_session 'one custom action' <<EOF
>>> todo.sh listaddons
foo
EOF
make_action "bar"
make_action "ls"
make_action "quux"
test_todo_session 'multiple custom actions' <<EOF
>>> todo.sh listaddons
bar
foo
ls
quux
EOF
chmod -x .todo.actions.d/foo
# On Cygwin, clearing the executable flag may have no effect, as the Windows ACL
# may still grant execution rights. In this case, we skip the test.
if [ -x .todo.actions.d/foo ]; then
SKIP_TESTS="${SKIP_TESTS}${SKIP_TESTS+ }t8010.4"
fi
test_todo_session 'nonexecutable action' <<EOF
>>> todo.sh listaddons
bar
ls
quux
EOF
test_done

View File

@@ -1,76 +0,0 @@
#!/bin/bash
test_description='custom actions help functionality
This test checks listing the usage help of a custom action.
'
. ./actions-test-lib.sh
. ./test-lib.sh
test_todo_session 'custom action help with no custom action directory' <<'EOF'
>>> todo.sh help foo
TODO: No action "foo" exists.
=== 1
EOF
make_action "foo"
make_action "bar"
make_action "ls"
make_action "quux"
test_todo_session 'custom action help' <<'EOF'
>>> todo.sh help foo
foo ITEM#[, ITEM#, ...] [TERM...]
This custom action does foo.
\
>>> todo.sh help bar
bar ITEM#[, ITEM#, ...] [TERM...]
This custom action does bar.
\
EOF
test_todo_session 'multiple custom actions help' <<'EOF'
>>> todo.sh help foo bar
foo ITEM#[, ITEM#, ...] [TERM...]
This custom action does foo.
\
bar ITEM#[, ITEM#, ...] [TERM...]
This custom action does bar.
\
EOF
test_todo_session 'nonexisting action help' <<'EOF'
>>> todo.sh help doesnotexist
TODO: No action "doesnotexist" exists.
=== 1
>>> todo.sh help foo doesnotexist bar
foo ITEM#[, ITEM#, ...] [TERM...]
This custom action does foo.
\
TODO: No action "doesnotexist" exists.
=== 1
EOF
test_todo_session 'mixed built-in and custom actions help' <<'EOF'
>>> todo.sh help foo shorthelp bar
foo ITEM#[, ITEM#, ...] [TERM...]
This custom action does foo.
\
shorthelp
List the one-line usage of all built-in and add-on actions.
\
bar ITEM#[, ITEM#, ...] [TERM...]
This custom action does bar.
\
EOF
test_todo_session 'custom override of built-in action help' <<'EOF'
>>> todo.sh help ls
ls ITEM#[, ITEM#, ...] [TERM...]
This custom action does ls.
\
EOF
test_done

View File

@@ -264,7 +264,25 @@ test_expect_success () {
test_expect_output () { test_expect_output () {
test "$#" = 2 || test "$#" = 2 ||
error "bug in the test script: not 2 parameters to test-expect-output" error "bug in the test script: not 2 parameters to test-expect-output"
test_expect_code_and_output 0 "$@" if ! test_skip "$@"
then
say >&3 "expecting success and output: $2"
test_run_ "$2"
if [ "$?" = 0 -a "$eval_ret" = 0 ]
then
cmp_output=$(test_cmp expect output)
if [ "$?" = 0 ]
then
test_ok_ "$1"
else
test_failure_ "$@" "
$cmp_output"
fi
else
test_failure_ "$@"
fi
fi
echo >&3 ""
} }
test_expect_code_and_output () { test_expect_code_and_output () {
@@ -272,11 +290,7 @@ test_expect_code_and_output () {
error "bug in the test script: not 3 parameters to test-expect-code-and-output" error "bug in the test script: not 3 parameters to test-expect-code-and-output"
if ! test_skip "$@" if ! test_skip "$@"
then then
if [ "$1" = 0 ]; then say >&3 "expecting exit code $1 and output: $3"
say >&3 "expecting success and output: $3"
else
say >&3 "expecting exit code $1 and output: $3"
fi
test_run_ "$3" test_run_ "$3"
if [ "$?" = 0 -a "$eval_ret" = "$1" ] if [ "$?" = 0 -a "$eval_ret" = "$1" ]
then then
@@ -552,16 +566,14 @@ test_tick () {
} }
# Generate and run a series of tests based on a transcript. # Generate and run a series of tests based on a transcript.
# Usage: test_todo_session "description" <<'EOF' # Usage: test_todo_session "description" <<EOF
# >>> command # >>> command
# output1 # output1
# output2 # output2
#
# >>> command # >>> command
# === exit status # === exit status
# output3 with empty line (must be escaped here) # output3
# \ # output4
# output5
# EOF # EOF
test_todo_session () { test_todo_session () {
test "$#" = 1 || test "$#" = 1 ||
@@ -570,7 +582,7 @@ test_todo_session () {
cmd="" cmd=""
status=0 status=0
> expect > expect
while IFS= read -r line while read -r line
do do
case $line in case $line in
">>> "*) ">>> "*)
@@ -594,9 +606,6 @@ test_todo_session () {
> expect > expect
fi fi
;; ;;
\\)
echo "" >> expect
;;
*) *)
echo "$line" >> expect echo "$line" >> expect
;; ;;

View File

@@ -2,7 +2,7 @@
# Your todo.txt directory # Your todo.txt directory
#export TODO_DIR="/Users/gina/Documents/todo" #export TODO_DIR="/Users/gina/Documents/todo"
export TODO_DIR=$(dirname "$0") export TODO_DIR=`dirname "$0"`
# Your todo/done/report.txt locations # Your todo/done/report.txt locations
export TODO_FILE="$TODO_DIR/todo.txt" export TODO_FILE="$TODO_DIR/todo.txt"

135
todo.sh
View File

@@ -54,11 +54,10 @@ shorthelp()
del|rm ITEM# [TERM] del|rm ITEM# [TERM]
depri|dp ITEM#[, ITEM#, ITEM#, ...] depri|dp ITEM#[, ITEM#, ITEM#, ...]
do ITEM#[, ITEM#, ITEM#, ...] do ITEM#[, ITEM#, ITEM#, ...]
help [ACTION...] help
list|ls [TERM...] list|ls [TERM...]
listall|lsa [TERM...] listall|lsa [TERM...]
listaddons listcon|lsc
listcon|lsc [TERM...]
listfile|lf [SRC [TERM...]] listfile|lf [SRC [TERM...]]
listpri|lsp [PRIORITIES] [TERM...] listpri|lsp [PRIORITIES] [TERM...]
listproj|lsprj [TERM...] listproj|lsprj [TERM...]
@@ -81,6 +80,7 @@ shorthelp()
See "help" for more details. See "help" for more details.
EndHelpFooter EndHelpFooter
exit 0
} }
help() help()
@@ -153,12 +153,6 @@ help()
EndVerboseHelp EndVerboseHelp
actionsHelp
addonHelp
}
actionsHelp()
{
cat <<-EndActionsHelp cat <<-EndActionsHelp
Built-in Actions: Built-in Actions:
add "THING I NEED TO DO +project @context" add "THING I NEED TO DO +project @context"
@@ -205,9 +199,8 @@ actionsHelp()
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.
help [ACTION...] help
Display help about usage, options, built-in and add-on actions, Display this help message.
or just the usage help for the passed ACTION(s).
list [TERM...] list [TERM...]
ls [TERM...] ls [TERM...]
@@ -226,13 +219,9 @@ actionsHelp()
TERM specified, lists entire todo.txt AND done.txt TERM specified, lists entire todo.txt AND done.txt
concatenated and sorted. concatenated and sorted.
listaddons listcon
Lists all added and overridden actions in the actions directory. lsc
listcon [TERM...]
lsc [TERM...]
Lists all the task contexts that start with the @ sign in todo.txt. Lists all the task contexts that start with the @ sign in todo.txt.
If TERM specified, considers only tasks that contain TERM(s).
listfile [SRC [TERM...]] listfile [SRC [TERM...]]
lf [SRC [TERM...]] lf [SRC [TERM...]]
@@ -252,11 +241,10 @@ actionsHelp()
Hides all tasks that contain TERM(s) preceded by a minus sign Hides all tasks that contain TERM(s) preceded by a minus sign
(i.e. -TERM). (i.e. -TERM).
listproj [TERM...] listproj
lsprj [TERM...] lsprj
Lists all the projects (terms that start with a + sign) in Lists all the projects (terms that start with a + sign) in
todo.txt. todo.txt.
If TERM specified, considers only tasks that contain TERM(s).
move ITEM# DEST [SRC] move ITEM# DEST [SRC]
mv ITEM# DEST [SRC] mv ITEM# DEST [SRC]
@@ -286,6 +274,9 @@ actionsHelp()
List the one-line usage of all built-in and add-on actions. List the one-line usage of all built-in and add-on actions.
EndActionsHelp EndActionsHelp
addonHelp
exit 1
} }
addonHelp() addonHelp()
@@ -307,35 +298,6 @@ addonHelp()
fi fi
} }
actionUsage()
{
for actionName
do
action="${TODO_ACTIONS_DIR}/${actionName}"
if [ -f "$action" -a -x "$action" ]; then
"$action" usage
else
builtinActionUsage=$(actionsHelp | sed -n -e "/^ ${actionName//\//\\/} /,/^\$/p" -e "/^ ${actionName//\//\\/}$/,/^\$/p")
if [ "$builtinActionUsage" ]; then
echo "$builtinActionUsage"
echo
else
die "TODO: No action \"${actionName}\" exists."
fi
fi
done
}
dieWithHelp()
{
case "$1" in
help) help;;
shorthelp) shorthelp;;
esac
shift
die "$@"
}
die() die()
{ {
echo "$*" echo "$*"
@@ -423,22 +385,21 @@ replaceOrPrepend()
else else
input=$* input=$*
fi fi
cleaninput "for sed"
# Retrieve existing priority and prepended date # Retrieve existing priority and prepended date
local -r priAndDateExpr='^\((.) \)\{0,1\}\([0-9]\{2,4\}-[0-9]\{2\}-[0-9]\{2\} \)\{0,1\}' priority=$(sed -e "$item!d" -e $item's/^\((.) \)\{0,1\}\([0-9]\{2,4\}-[0-9]\{2\}-[0-9]\{2\} \)\{0,1\}.*/\1/' "$TODO_FILE")
priority=$(sed -e "$item!d" -e "${item}s/${priAndDateExpr}.*/\\1/" "$TODO_FILE") prepdate=$(sed -e "$item!d" -e $item's/^\((.) \)\{0,1\}\([0-9]\{2,4\}-[0-9]\{2\}-[0-9]\{2\} \)\{0,1\}.*/\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" -a "$action" = "replace" ] && [ "$(echo "$input"|sed -e 's/^\([0-9]\{2,4\}-[0-9]\{2\}-[0-9]\{2\}\)\{0,1\}.*/\1/')" ]; then
# If the replaced text starts with a [priority +] date, it will replace # If the replaced text starts with a date, it will replace the existing
# the existing date, too. # date, too.
prepdate= prepdate=
fi fi
# Temporarily remove any existing priority and prepended date, perform the # Temporarily remove any existing priority and prepended date, perform the
# change (replace/prepend) and re-insert the existing priority and prepended # change (replace/prepend) and re-insert the existing priority and prepended
# date again. # date again.
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"
@@ -523,7 +484,6 @@ do
# 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
;; ;;
n ) n )
OVR_TODOTXT_PRESERVE_LINE_NUMBERS=0 OVR_TODOTXT_PRESERVE_LINE_NUMBERS=0
@@ -664,7 +624,7 @@ fi
} }
# === SANITY CHECKS (thanks Karl!) === # === SANITY CHECKS (thanks Karl!) ===
[ -r "$TODOTXT_CFG_FILE" ] || dieWithHelp "$1" "Fatal Error: Cannot read configuration file $TODOTXT_CFG_FILE" [ -r "$TODOTXT_CFG_FILE" ] || die "Fatal Error: Cannot read configuration file $TODOTXT_CFG_FILE"
. "$TODOTXT_CFG_FILE" . "$TODOTXT_CFG_FILE"
@@ -703,8 +663,8 @@ fi
ACTION=${1:-$TODOTXT_DEFAULT_ACTION} ACTION=${1:-$TODOTXT_DEFAULT_ACTION}
[ -z "$ACTION" ] && usage [ -z "$ACTION" ] && usage
[ -d "$TODO_DIR" ] || dieWithHelp "$1" "Fatal Error: $TODO_DIR is not a directory" [ -d "$TODO_DIR" ] || die "Fatal Error: $TODO_DIR is not a directory"
( cd "$TODO_DIR" ) || dieWithHelp "$1" "Fatal Error: Unable to cd to $TODO_DIR" ( cd "$TODO_DIR" ) || die "Fatal Error: Unable to cd to $TODO_DIR"
[ -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"
@@ -894,17 +854,7 @@ _format()
fi fi
} }
listWordsWithSigil() export -f cleaninput getPrefix getTodo getNewtodo shellquote filtercommand _list getPadding _format die
{
sigil=$1
shift
FILE=$TODO_FILE
[ "$TODOTXT_SOURCEVAR" ] && eval "FILE=$TODOTXT_SOURCEVAR"
eval "$(filtercommand 'cat "${FILE[@]}"' '' "$@")" | grep -o "[^ ]*${sigil}[^ ]\\+" | grep "^$sigil" | sort -u
}
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 'A-Z' 'a-z' )
@@ -1121,19 +1071,13 @@ case $action in
;; ;;
"help" ) "help" )
shift ## Was help; new $1 is first help topic / action name if [ -t 1 ] ; then # STDOUT is a TTY
if [ $# -gt 0 ]; then if which "${PAGER:-less}" >/dev/null 2>&1; then
# Don't use PAGER here; we don't expect much usage output from one / few actions. # we have a working PAGER (or less as a default)
actionUsage "$@" help | "${PAGER:-less}" && exit 0
else
if [ -t 1 ] ; then # STDOUT is a TTY
if which "${PAGER:-less}" >/dev/null 2>&1; then
# we have a working PAGER (or less as a default)
help | "${PAGER:-less}" && exit 0
fi
fi fi
help # just in case something failed above, we go ahead and just spew to STDOUT
fi fi
help # just in case something failed above, we go ahead and just spew to STDOUT
;; ;;
"shorthelp" ) "shorthelp" )
@@ -1157,7 +1101,7 @@ case $action in
TOTAL=$( sed -n '$ =' "$TODO_FILE" ) TOTAL=$( sed -n '$ =' "$TODO_FILE" )
PADDING=${#TOTAL} PADDING=${#TOTAL}
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="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
@@ -1185,20 +1129,23 @@ case $action in
;; ;;
"listcon" | "lsc" ) "listcon" | "lsc" )
shift FILE=$TODO_FILE
listWordsWithSigil '@' "$@" [ "$TODOTXT_SOURCEVAR" ] && eval "FILE=$TODOTXT_SOURCEVAR"
grep -ho '[^ ]*@[^ ]\+' "${FILE[@]}" | grep '^@' | sort -u
;; ;;
"listproj" | "lsprj" ) "listproj" | "lsprj" )
FILE=$TODO_FILE
[ "$TODOTXT_SOURCEVAR" ] && eval "FILE=$TODOTXT_SOURCEVAR"
shift shift
listWordsWithSigil '+' "$@" eval "$(filtercommand 'cat "${FILE[@]}"' '' "$@")" | grep -o '[^ ]*+[^ ]\+' | grep '^+' | sort -u
;; ;;
"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 'a-z' 'A-Z' | 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="grep '^ *[0-9]\+ ([${pri}]) '"
_list "$TODO_FILE" "$@" _list "$TODO_FILE" "$@"
;; ;;
@@ -1353,18 +1300,6 @@ note: PRIORITY must be anywhere from A to Z."
fi fi
;; ;;
"listaddons" )
if [ -d "$TODO_ACTIONS_DIR" ]; then
cd "$TODO_ACTIONS_DIR" || exit $?
for action in *
do
if [ -f "$action" -a -x "$action" ]; then
echo "$action"
fi
done
fi
;;
* ) * )
usage;; usage;;
esac esac

View File

@@ -11,29 +11,30 @@ _todo()
local -r OPTS="-@ -@@ -+ -++ -d -f -h -p -P -PP -a -n -t -v -vv -V -x" local -r OPTS="-@ -@@ -+ -++ -d -f -h -p -P -PP -a -n -t -v -vv -V -x"
local -r COMMANDS="\ local -r COMMANDS="\
add a addto addm append app archive command del \ add a addto addm append app archive command del \
rm depri dp do help list ls listaddons listall lsa listcon \ rm depri dp do help list ls listall lsa listcon \
lsc listfile lf listpri lsp listproj lsprj move \ lsc listfile lf listpri lsp listproj lsprj move \
mv prepend prep pri p replace report shorthelp" mv prepend prep pri p replace report shorthelp"
local -r MOVE_COMMAND_PATTERN='^(move|mv)$'
# Add custom commands from add-ons, if installed.
# TODO: Filter for executable flag of files found in $TODO_ACTIONS_DIR.
local allCommands="$COMMANDS $('ls' "${TODO_ACTIONS_DIR:-$HOME/.todo.actions.d}/" 2>/dev/null)"
local _todo_sh=${_todo_sh:-todo.sh} local _todo_sh=${_todo_sh:-todo.sh}
local completions local completions
if [ $COMP_CWORD -eq 1 ]; then if [ $COMP_CWORD -eq 1 ]; then
completions="$COMMANDS $(eval TODOTXT_VERBOSE=0 $_todo_sh command listaddons) $OPTS" completions="$allCommands $OPTS"
elif [[ $COMP_CWORD -gt 2 && ( \ elif [[ $COMP_CWORD -gt 2 && ( \
"${COMP_WORDS[COMP_CWORD-2]}" =~ $MOVE_COMMAND_PATTERN || \ "${COMP_WORDS[COMP_CWORD-2]}" =~ ^(move|mv)$ || \
"${COMP_WORDS[COMP_CWORD-3]}" =~ $MOVE_COMMAND_PATTERN ) ]]; then "${COMP_WORDS[COMP_CWORD-3]}" =~ ^(move|mv)$ ) ]]; then
# "move ITEM# DEST [SRC]" has file arguments on positions 2 and 3. # "move ITEM# DEST [SRC]" has file arguments on positions 2 and 3.
completions=$(eval TODOTXT_VERBOSE=0 $_todo_sh command listfile) completions=$(eval TODOTXT_VERBOSE=0 $_todo_sh command listfile)
else else
case "$prev" in case "$prev" in
command) command)
completions=$COMMANDS;; completions=$COMMANDS;;
help)
completions="$COMMANDS $(eval TODOTXT_VERBOSE=0 $_todo_sh command listaddons)";;
addto|listfile|lf) addto|listfile|lf)
completions=$(eval TODOTXT_VERBOSE=0 $_todo_sh command listfile);; completions=$(eval TODOTXT_VERBOSE=0 $_todo_sh command listfile);;
-*) completions="$COMMANDS $(eval TODOTXT_VERBOSE=0 $_todo_sh command listaddons) $OPTS";; -*) completions="$allCommands $OPTS";;
*) case "$cur" in *) case "$cur" in
+*) completions=$(eval TODOTXT_VERBOSE=0 $_todo_sh command listproj) +*) completions=$(eval TODOTXT_VERBOSE=0 $_todo_sh command listproj)
COMPREPLY=( $( compgen -W "$completions" -- $cur )) COMPREPLY=( $( compgen -W "$completions" -- $cur ))
@@ -61,8 +62,8 @@ _todo()
# a safety check of the ls action output. # a safety check of the ls action output.
local todo=$( \ local todo=$( \
eval TODOTXT_VERBOSE=0 $_todo_sh '-@ -+ -p -x command ls "^ *${cur} "' | \ eval TODOTXT_VERBOSE=0 $_todo_sh '-@ -+ -p -x command ls "^ *${cur} "' | \
sed -e 's/^ *[0-9]\{1,\} //' -e 's/^\((.) \)\{0,1\}[0-9]\{2,4\}-[0-9]\{2\}-[0-9]\{2\} /\1/' \ sed -e 's/^ *[0-9]\+ //' -e 's/\((.) \)[0-9]\{2,4\}-[0-9]\{2\}-[0-9]\{2\} /\1/' \
-e 's/^\([xX] \)\([0-9]\{2,4\}-[0-9]\{2\}-[0-9]\{2\} \)\{1,2\}/\1/' \ -e 's/\([xX] \)\([0-9]\{2,4\}-[0-9]\{2\}-[0-9]\{2\} \)\{1,2\}/\1/' \
-e 's/[[:space:]]*$//' \ -e 's/[[:space:]]*$//' \
-e '1q' \ -e '1q' \
) )
@@ -88,9 +89,6 @@ complete -F _todo todo.sh
# If you define an alias (e.g. "t") to todo.sh, you need to explicitly enable # If you define an alias (e.g. "t") to todo.sh, you need to explicitly enable
# completion for it, too: # completion for it, too:
#complete -F _todo t #complete -F _todo t
# It is recommended to put this line next to your alias definition in your
# ~/.bashrc (or wherever else you're defining your alias). If you simply
# uncomment it here, you will need to redo this on every todo.txt update!
# If you have renamed the todo.sh executable, or if it is not accessible through # If you have renamed the todo.sh executable, or if it is not accessible through
# PATH, you need to add and use a wrapper completion function, like this: # PATH, you need to add and use a wrapper completion function, like this: