Merge remote-tracking branch 'upstream/master'
This commit is contained in:
6
.github/dependabot.yml
vendored
Normal file
6
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: github-actions
|
||||
directory: /
|
||||
schedule:
|
||||
interval: weekly
|
||||
5
.github/workflows/tests.yml
vendored
5
.github/workflows/tests.yml
vendored
@@ -5,15 +5,16 @@ on:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
workflow_dispatch: # Allows you to run this workflow manually from the Actions tab
|
||||
|
||||
jobs:
|
||||
test:
|
||||
strategy:
|
||||
matrix:
|
||||
platform: [ubuntu-20.04, macos-11]
|
||||
platform: [ubuntu-24.04, macos-14]
|
||||
runs-on: ${{ matrix.platform }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- run: make
|
||||
- run: make dist
|
||||
- run: make test
|
||||
|
||||
2
Makefile
2
Makefile
@@ -64,7 +64,7 @@ clean: test-pre-clean
|
||||
|
||||
install: installdirs
|
||||
$(INSTALL_PROGRAM) todo.sh $(DESTDIR)$(bindir)/todo.sh
|
||||
$(INSTALL_DATA) todo_completion $(DESTDIR)$(datarootdir)/todo
|
||||
$(INSTALL_DATA) todo_completion $(DESTDIR)$(datarootdir)/todo.sh
|
||||
[ -e $(DESTDIR)$(sysconfdir)/todo/config ] || \
|
||||
sed "s/^\(export[ \t]*TODO_DIR=\).*/\1~\/.todo/" todo.cfg > $(DESTDIR)$(sysconfdir)/todo/config
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ make test
|
||||
*NOTE:* Makefile defaults to several default paths for installed files. Adjust to your system:
|
||||
|
||||
- `INSTALL_DIR`: PATH for executables (default /usr/local/bin)
|
||||
- `CONFIG_DIR`: PATH for todo.txt config
|
||||
- `CONFIG_DIR`: PATH for the todo.txt configuration template
|
||||
- `BASH_COMPLETION`: PATH for autocompletion scripts (default to /etc/bash_completion.d)
|
||||
|
||||
```shell
|
||||
@@ -58,6 +58,11 @@ make install CONFIG_DIR=/etc INSTALL_DIR=/usr/bin BASH_COMPLETION=/usr/share/bas
|
||||
https://aur.archlinux.org/packages/todotxt/
|
||||
|
||||
|
||||
## Configuration
|
||||
|
||||
No configuration is required; however, most users tweak the default settings (e.g. relocating the todo.txt directory to a subdirectory of the user's home directory, or onto a cloud drive (via the `TODO_DIR` variable)), modify the colors, add additional highlighting of projects, contexts, dates, and so on. A configuration template with a commented-out list of all available options is included.
|
||||
It is recommended to _copy_ that template into one of the locations listed by `todo.sh help` on `-d CONFIG_FILE`, even if it is installed in the global configuration location (`/etc/todo/config`).
|
||||
|
||||
## Usage
|
||||
```shell
|
||||
todo.sh [-fhpantvV] [-d todo_config] action [task_number] [task_description]
|
||||
|
||||
@@ -1,20 +1,26 @@
|
||||
#!/bin/bash
|
||||
|
||||
make_dummy_action()
|
||||
{
|
||||
local actionName; actionName="$(basename "${1:?}")"
|
||||
cat > "$1" <<EOF
|
||||
#!/bin/bash
|
||||
[ "\$1" = "usage" ] && {
|
||||
echo " $actionName ITEM#[, ITEM#, ...] [TERM...]"
|
||||
echo " This custom action does $actionName."
|
||||
echo ""
|
||||
exit
|
||||
}
|
||||
echo "custom action $actionName$2"
|
||||
EOF
|
||||
chmod +x "$1"
|
||||
}
|
||||
|
||||
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"
|
||||
[ -z "$1" ] || make_dummy_action ".todo.actions.d/$1"
|
||||
}
|
||||
|
||||
make_action_in_folder()
|
||||
@@ -22,15 +28,21 @@ make_action_in_folder()
|
||||
unset TODO_ACTIONS_DIR
|
||||
[ -d .todo.actions.d ] || mkdir .todo.actions.d
|
||||
mkdir ".todo.actions.d/$1"
|
||||
cat > ".todo.actions.d/$1/$1" <<EOF
|
||||
#!/bin/bash
|
||||
[ "\$1" = "usage" ] && {
|
||||
echo " $1 ITEM#[, ITEM#, ...] [TERM...]"
|
||||
echo " This custom action does $1."
|
||||
echo ""
|
||||
exit
|
||||
[ -z "$1" ] || make_dummy_action ".todo.actions.d/$1/$1" "in folder $1"
|
||||
}
|
||||
echo "custom action $1 in folder $1"
|
||||
EOF
|
||||
chmod +x ".todo.actions.d/$1/$1"
|
||||
|
||||
invalidate_action()
|
||||
{
|
||||
local customActionFilespec="${1:?}"; shift
|
||||
local testName="${1:?}"; shift
|
||||
|
||||
chmod -x "$customActionFilespec"
|
||||
# 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, and
|
||||
# remove the (still valid) custom action so that it doesn't break following
|
||||
# tests.
|
||||
if [ -x "$customActionFilespec" ]; then
|
||||
SKIP_TESTS="${SKIP_TESTS}${SKIP_TESTS+ }${testName}"
|
||||
rm -- "$customActionFilespec"
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -140,6 +140,18 @@ TODO: Replaced task with:
|
||||
1 2010-07-04 this also has a new date
|
||||
EOF
|
||||
|
||||
cat /dev/null > todo.txt
|
||||
test_todo_session 'replace handling prepended priority on add' <<EOF
|
||||
>>> todo.sh -t add "new task"
|
||||
1 2009-02-13 new task
|
||||
TODO: 1 added.
|
||||
|
||||
>>> todo.sh replace 1 '(B) this also has a priority now'
|
||||
1 2009-02-13 new task
|
||||
TODO: Replaced task with:
|
||||
1 (B) 2009-02-13 this also has a priority now
|
||||
EOF
|
||||
|
||||
cat /dev/null > todo.txt
|
||||
test_todo_session 'replace handling priority and prepended date on add' <<EOF
|
||||
>>> todo.sh -t add "new task"
|
||||
@@ -156,6 +168,18 @@ TODO: Replaced task with:
|
||||
1 (A) 2009-02-13 this is just a new one
|
||||
EOF
|
||||
|
||||
cat /dev/null > todo.txt
|
||||
test_todo_session 'replace handling prepended priority and date on add' <<EOF
|
||||
>>> todo.sh -t add "new task"
|
||||
1 2009-02-13 new task
|
||||
TODO: 1 added.
|
||||
|
||||
>>> todo.sh replace 1 '(C) 2010-07-04 this also has a priority and new date'
|
||||
1 2009-02-13 new task
|
||||
TODO: Replaced task with:
|
||||
1 (C) 2010-07-04 this also has a priority and new date
|
||||
EOF
|
||||
|
||||
echo '(A) 2009-02-13 this is just a new one' > todo.txt
|
||||
test_todo_session 'replace with prepended date replaces existing date' <<EOF
|
||||
>>> todo.sh replace 1 2010-07-04 this also has a new date
|
||||
@@ -164,6 +188,14 @@ TODO: Replaced task with:
|
||||
1 (A) 2010-07-04 this also has a new date
|
||||
EOF
|
||||
|
||||
echo '(A) 2009-02-13 this is just a new one' > todo.txt
|
||||
test_todo_session 'replace with prepended priority replaces existing priority' <<EOF
|
||||
>>> todo.sh replace 1 '(B) this also has a new priority'
|
||||
1 (A) 2009-02-13 this is just a new one
|
||||
TODO: Replaced task with:
|
||||
1 (B) 2009-02-13 this also has a new priority
|
||||
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'
|
||||
@@ -172,4 +204,13 @@ TODO: Replaced task with:
|
||||
1 (B) 2010-07-04 this also has a new date
|
||||
EOF
|
||||
|
||||
|
||||
echo '(A) 2009-02-13 this is just a new one' > todo.txt
|
||||
test_todo_session 'replace with prepended priority and date replaces existing priority and date' <<EOF
|
||||
>>> todo.sh replace 1 '(B) 2010-07-04 this also has a new prio+date'
|
||||
1 (A) 2009-02-13 this is just a new one
|
||||
TODO: Replaced task with:
|
||||
1 (B) 2010-07-04 this also has a new prio+date
|
||||
EOF
|
||||
|
||||
test_done
|
||||
|
||||
@@ -90,6 +90,7 @@ TODO: 2 re-prioritized from (C) to (A).
|
||||
TODO: 3 of 3 tasks shown
|
||||
|
||||
>>> todo.sh pri 2 a
|
||||
=== 1
|
||||
2 (A) notice the sunflowers
|
||||
TODO: 2 already prioritized (A).
|
||||
|
||||
|
||||
@@ -96,6 +96,26 @@ TODO: 0 of 5 tasks shown
|
||||
--
|
||||
TODO: 1 of 5 tasks shown
|
||||
EOF
|
||||
test_todo_session 'listpri filtering concatenation of priorities and -ranges' <<EOF
|
||||
>>> todo.sh -p listpri CX
|
||||
3 (C) notice the sunflowers
|
||||
2 (X) clean the house from A-Z
|
||||
4 (X) listen to music
|
||||
--
|
||||
TODO: 3 of 5 tasks shown
|
||||
|
||||
>>> todo.sh -p listpri ABR-Y
|
||||
1 (B) smell the uppercase Roses +flowers @outside
|
||||
2 (X) clean the house from A-Z
|
||||
4 (X) listen to music
|
||||
--
|
||||
TODO: 3 of 5 tasks shown
|
||||
|
||||
>>> todo.sh -p listpri A-
|
||||
2 (X) clean the house from A-Z
|
||||
--
|
||||
TODO: 1 of 5 tasks shown
|
||||
EOF
|
||||
|
||||
cat > todo.txt <<EOF
|
||||
(B) ccc xxx this line should be third.
|
||||
|
||||
@@ -81,6 +81,7 @@ test_todo_session 'fail multiple do attempts' <<EOF
|
||||
TODO: 3 marked as done.
|
||||
|
||||
>>> todo.sh -a do 3
|
||||
=== 1
|
||||
TODO: 3 is already marked done.
|
||||
EOF
|
||||
|
||||
|
||||
@@ -82,6 +82,7 @@ test_todo_session 'depriority of unprioritized task' <<EOF
|
||||
TODO: 3 of 3 tasks shown
|
||||
|
||||
>>> todo.sh depri 3 2
|
||||
=== 1
|
||||
TODO: 3 is not prioritized.
|
||||
2 notice the sunflowers
|
||||
TODO: 2 deprioritized.
|
||||
|
||||
@@ -60,8 +60,9 @@ test_todo_session 'del with confirmation' <<EOF
|
||||
TODO: 3 of 3 tasks shown
|
||||
|
||||
>>> printf n | todo.sh del 1
|
||||
Delete '(B) smell the uppercase Roses +flowers @outside'? (y/n)$SPACE
|
||||
\\
|
||||
TODO: No tasks were deleted.
|
||||
=== 1
|
||||
|
||||
>>> todo.sh -p list
|
||||
2 (A) notice the sunflowers
|
||||
@@ -71,15 +72,17 @@ TODO: No tasks were deleted.
|
||||
TODO: 3 of 3 tasks shown
|
||||
|
||||
>>> printf x | todo.sh del 1
|
||||
Delete '(B) smell the uppercase Roses +flowers @outside'? (y/n)$SPACE
|
||||
\\
|
||||
TODO: No tasks were deleted.
|
||||
=== 1
|
||||
|
||||
>>> echo | todo.sh del 1
|
||||
Delete '(B) smell the uppercase Roses +flowers @outside'? (y/n)$SPACE
|
||||
\\
|
||||
TODO: No tasks were deleted.
|
||||
=== 1
|
||||
|
||||
>>> printf y | todo.sh del 1
|
||||
Delete '(B) smell the uppercase Roses +flowers @outside'? (y/n)$SPACE
|
||||
\\
|
||||
1 (B) smell the uppercase Roses +flowers @outside
|
||||
TODO: 1 deleted.
|
||||
|
||||
|
||||
@@ -4,8 +4,6 @@ test_description='basic move functionality
|
||||
'
|
||||
. ./test-lib.sh
|
||||
|
||||
SPACE=' '
|
||||
|
||||
cat > todo.txt <<EOF
|
||||
(B) smell the uppercase Roses +flowers @outside
|
||||
(A) notice the sunflowers
|
||||
@@ -42,7 +40,7 @@ x 2009-02-13 smell the coffee +wakeup
|
||||
EOF
|
||||
test_todo_session 'basic move with confirmation' <<EOF
|
||||
>>> printf y | todo.sh move 1 done.txt 2>&1 | sed -e "s#'[^']\{1,\}/\([^/']\{1,\}\)'#'\1'#g" -e 's#from .\{1,\}/\([^/]\{1,\}\) to .\{1,\}/\([^/]\{1,\}\)?#from \1 to \2?#g'
|
||||
Move '(B) smell the uppercase Roses +flowers @outside' from todo.txt to done.txt? (y/n)$SPACE
|
||||
\\
|
||||
1 (B) smell the uppercase Roses +flowers @outside
|
||||
TODO: 1 moved from 'todo.txt' to 'done.txt'.
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ EOF
|
||||
|
||||
test_todo_session 'deduplicate without duplicates' <<EOF
|
||||
>>> todo.sh deduplicate
|
||||
=== 1
|
||||
TODO: No duplicate tasks found
|
||||
EOF
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ echo 'export TODO_ACTIONS_DIR=$HOME/custom.actions' >> custom.cfg
|
||||
export TODOTXT_GLOBAL_CFG_FILE=global.cfg
|
||||
|
||||
test_todo_session '-h and fatal error without config' <<EOF
|
||||
>>> todo.sh -h | sed '/^ \\{0,2\\}[A-Z]/!d'
|
||||
>>> todo.sh -h 2>&1 | 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
|
||||
@@ -58,7 +58,7 @@ 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'
|
||||
>>> todo.sh -h -d custom.cfg 2>&1 | 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
|
||||
@@ -69,7 +69,7 @@ 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'
|
||||
>>> todo.sh -d custom.cfg -h 2>&1 | 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
|
||||
|
||||
@@ -46,15 +46,22 @@ test_todo_session 'todo 1 and 2 contexts' <<EOF
|
||||
EOF
|
||||
|
||||
# Define a second completion function that injects the different configuration
|
||||
# file. In real use, this would be installed via
|
||||
# file and uppercases all output. (This is a silly behavior change that still
|
||||
# requires a completion function override.)
|
||||
# In real use, this would be installed via
|
||||
# complete -F _todo2 todo2
|
||||
_uppercase_todo()
|
||||
{
|
||||
todo.sh "$@" | tr '[:lower:]' '[:upper:]'
|
||||
}
|
||||
_todo2()
|
||||
{
|
||||
local _todo_sh='todo.sh -d "$HOME/todo2.cfg"'
|
||||
local _todo_sh='_uppercase_todo -d "$HOME/todo2.cfg"'
|
||||
_todo "$@"
|
||||
}
|
||||
|
||||
test_todo_completion 'all todo1 contexts' 'todo1 list @' '@garden @outdoor @outside'
|
||||
test_todo_custom_completion _todo2 'all todo2 contexts' 'todo2 list @' '@home @oriental'
|
||||
test_todo_completion 'all todo2 contexts' 'todo2 list @' '@home @oriental'
|
||||
test_todo_custom_completion _todo2 'all uppercased todo2 contexts' 'doesNotMatter list @' '@HOME @ORIENTAL'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -44,4 +44,50 @@ custom action bad
|
||||
=== 42
|
||||
EOF
|
||||
|
||||
make_action
|
||||
ln -s /actionsdir/doesnotexist/badlink .todo.actions.d/badlink
|
||||
# On Cygwin, the Windows ACL may still grant execution rights. In this case, we
|
||||
# skip the test.
|
||||
if [ -x .todo.actions.d/badlink ]; then
|
||||
SKIP_TESTS="${SKIP_TESTS}${SKIP_TESTS+ }t8000.6 t8000.7"
|
||||
fi
|
||||
test_todo_session 'broken symlink' <<EOF
|
||||
>>> todo.sh badlink 2>&1 | sed "s#'[^']*\(\\.todo\\.actions\\.d/[^']\{1,\}\)'#'\1'#g"
|
||||
Fatal Error: Broken link to custom action: '.todo.actions.d/badlink'
|
||||
|
||||
>>> todo.sh do 2>/dev/null
|
||||
=== 1
|
||||
EOF
|
||||
|
||||
make_action
|
||||
mkdir .todo.actions.d/badfolderlink
|
||||
ln -s /actionsdir/doesnotexist/badfolderlink .todo.actions.d/badfolderlink/badfolderlink
|
||||
# On Cygwin, the Windows ACL may still grant execution rights. In this case, we
|
||||
# skip the test.
|
||||
if [ -x .todo.actions.d/badfolderlink/badfolderlink ]; then
|
||||
SKIP_TESTS="${SKIP_TESTS}${SKIP_TESTS+ }t8000.8 t8000.9"
|
||||
fi
|
||||
test_todo_session 'broken symlink in folder' <<EOF
|
||||
>>> todo.sh badfolderlink 2>&1 | sed "s#'[^']*\(\\.todo\\.actions\\.d/[^']\{1,\}\)'#'\1'#g"
|
||||
Fatal Error: Broken link to custom action: '.todo.actions.d/badfolderlink/badfolderlink'
|
||||
|
||||
>>> todo.sh do 2>/dev/null
|
||||
=== 1
|
||||
EOF
|
||||
|
||||
make_action
|
||||
ln -s /actionsdir/doesnotexist/do .todo.actions.d/do
|
||||
# On Cygwin, the Windows ACL may still grant execution rights. In this case, we
|
||||
# skip the test.
|
||||
if [ -x .todo.actions.d/do ]; then
|
||||
SKIP_TESTS="${SKIP_TESTS}${SKIP_TESTS+ }t8000.10 t8000.11"
|
||||
fi
|
||||
test_todo_session 'broken symlink overrides built-in action' <<EOF
|
||||
>>> todo.sh do 2>&1 | sed "s#'[^']*\(\\.todo\\.actions\\.d/[^']\{1,\}\)'#'\1'#g"
|
||||
Fatal Error: Broken link to custom action: '.todo.actions.d/do'
|
||||
|
||||
>>> todo.sh do 2>/dev/null
|
||||
=== 1
|
||||
EOF
|
||||
|
||||
test_done
|
||||
|
||||
@@ -28,12 +28,7 @@ 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
|
||||
invalidate_action .todo.actions.d/foo t8010.4
|
||||
test_todo_session 'nonexecutable action' <<EOF
|
||||
>>> todo.sh listaddons
|
||||
bar
|
||||
@@ -66,13 +61,7 @@ norris
|
||||
quux
|
||||
EOF
|
||||
|
||||
# nthorne: shamelessly stolen from above..
|
||||
chmod -x .todo.actions.d/norris/norris
|
||||
# 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/norris/norris ]; then
|
||||
SKIP_TESTS="${SKIP_TESTS}${SKIP_TESTS+ }t8010.8"
|
||||
fi
|
||||
invalidate_action .todo.actions.d/norris/norris t8010.8
|
||||
test_todo_session 'nonexecutable action in subfolder' <<EOF
|
||||
>>> todo.sh listaddons
|
||||
bar
|
||||
|
||||
7
todo.cfg
7
todo.cfg
@@ -80,6 +80,13 @@ export REPORT_FILE="$TODO_DIR/report.txt"
|
||||
|
||||
# === BEHAVIOR ===
|
||||
|
||||
## verbosity
|
||||
#
|
||||
# By default, additional information and confirmation of actions (like
|
||||
# "TODO: 1 added") are printed. You can suppress this via 0 or add extra
|
||||
# verbosity via 2.
|
||||
# export TODOTXT_VERBOSE=1
|
||||
|
||||
## customize list output
|
||||
#
|
||||
# TODOTXT_SORT_COMMAND will filter after line numbers are
|
||||
|
||||
95
todo.sh
95
todo.sh
@@ -254,7 +254,7 @@ actionsHelp()
|
||||
listpri [PRIORITIES] [TERM...]
|
||||
lsp [PRIORITIES] [TERM...]
|
||||
Displays all tasks prioritized PRIORITIES.
|
||||
PRIORITIES can be a single one (A) or a range (A-C).
|
||||
PRIORITIES can be a [concatenation of] single (A) or range (A-C).
|
||||
If no PRIORITIES specified, lists all prioritized tasks.
|
||||
If TERM specified, lists only prioritized tasks that contain TERM(s).
|
||||
Hides all tasks that contain TERM(s) preceded by a minus sign
|
||||
@@ -349,14 +349,14 @@ dieWithHelp()
|
||||
case "$1" in
|
||||
help) help;;
|
||||
shorthelp) shorthelp;;
|
||||
esac
|
||||
esac >&2
|
||||
shift
|
||||
|
||||
die "$@"
|
||||
}
|
||||
die()
|
||||
{
|
||||
echo "$*"
|
||||
echo >&2 "$*"
|
||||
exit 1
|
||||
}
|
||||
|
||||
@@ -364,12 +364,11 @@ confirm()
|
||||
{
|
||||
[ $TODOTXT_FORCE = 0 ] || return 0
|
||||
|
||||
printf %s "${1:?}? (y/n) "
|
||||
local readArgs=(-e -r)
|
||||
[ -n "${BASH_VERSINFO:-}" ] && [ \( ${BASH_VERSINFO[0]} -eq 4 -a ${BASH_VERSINFO[1]} -ge 1 \) -o ${BASH_VERSINFO[0]} -gt 4 ] &&
|
||||
readArgs+=(-N 1) # Bash 4.1+ supports -N nchars
|
||||
local answer
|
||||
read "${readArgs[@]}" answer
|
||||
read -p "${1:?}? (y/n) " "${readArgs[@]}" answer
|
||||
echo
|
||||
[ "$answer" = "y" ]
|
||||
}
|
||||
@@ -451,28 +450,38 @@ replaceOrPrepend()
|
||||
getTodo "$item"
|
||||
|
||||
if [[ -z "$1" && $TODOTXT_FORCE = 0 ]]; then
|
||||
echo -n "$querytext"
|
||||
read -r -i "$todo" -e input
|
||||
read -p "$querytext" -r -i "$todo" -e input
|
||||
else
|
||||
input=$*
|
||||
fi
|
||||
|
||||
# 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/${priAndDateExpr}.*/\\1/" "$TODO_FILE")
|
||||
prepdate=$(sed -e "$item!d" -e "${item}s/${priAndDateExpr}.*/\\2/" "$TODO_FILE")
|
||||
|
||||
if [ "$prepdate" ] && [ "$action" = "replace" ] && [ "$(echo "$input"|sed -e "s/${priAndDateExpr}.*/\\1\\2/")" ]; then
|
||||
originalPriority=$(sed -e "$item!d" -e "${item}s/${priAndDateExpr}.*/\\1/" "$TODO_FILE")
|
||||
priority="$originalPriority"
|
||||
originalPrepdate=$(sed -e "$item!d" -e "${item}s/${priAndDateExpr}.*/\\2/" "$TODO_FILE")
|
||||
prepdate="$originalPrepdate"
|
||||
if [ "$action" = "replace" ]; then
|
||||
replacementPrepdate="$(echo "$input"|sed -e "s/${priAndDateExpr}.*/\\2/")"
|
||||
if [ "$replacementPrepdate" ]; then
|
||||
# If the replaced text starts with a [priority +] date, it will replace
|
||||
# the existing date, too.
|
||||
prepdate=
|
||||
prepdate="$replacementPrepdate"
|
||||
fi
|
||||
replacementPriority="$(echo "$input"|sed -e "s/${priAndDateExpr}.*/\\1/")"
|
||||
if [ "$replacementPriority" ]; then
|
||||
# If the replaced text starts with a priority, it will replace
|
||||
# the existing priority, too.
|
||||
priority="$replacementPriority"
|
||||
fi
|
||||
input="$(echo "$input"|sed -e "s/${priAndDateExpr}//")"
|
||||
fi
|
||||
|
||||
# Temporarily remove any existing priority and prepended date, perform the
|
||||
# change (replace/prepend) and re-insert the existing priority and prepended
|
||||
# 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/^${originalPriority}${originalPrepdate}//" -e "$item s|^.*|${priority}${prepdate}${input}${backref}|" "$TODO_FILE"
|
||||
if [ "$TODOTXT_VERBOSE" -gt 0 ]; then
|
||||
getNewtodo "$item"
|
||||
case "$action" in
|
||||
@@ -826,11 +835,6 @@ _addto() {
|
||||
fi
|
||||
}
|
||||
|
||||
shellquote()
|
||||
{
|
||||
typeset -r qq=\'; printf %s\\n "'${1//\'/${qq}\\${qq}${qq}}'";
|
||||
}
|
||||
|
||||
filtercommand()
|
||||
{
|
||||
filter=${1:-}
|
||||
@@ -845,13 +849,13 @@ filtercommand()
|
||||
then
|
||||
## First character isn't a dash: hide lines that don't match
|
||||
## this $search_term
|
||||
filter="${filter:-}${filter:+ | }grep -i $(shellquote "$search_term")"
|
||||
printf -v filter '%sgrep -i %q' "${filter:-}${filter:+ | }" "$search_term"
|
||||
else
|
||||
## First character is a dash: hide lines that match this
|
||||
## $search_term
|
||||
#
|
||||
## Remove the first character (-) before adding to our filter command
|
||||
filter="${filter:-}${filter:+ | }grep -v -i $(shellquote "${search_term:1}")"
|
||||
printf -v filter '%sgrep -v -i %q' "${filter:-}${filter:+ | }" "${search_term:1}"
|
||||
fi
|
||||
done
|
||||
|
||||
@@ -1045,7 +1049,18 @@ listWordsWithSigil()
|
||||
| sort -u
|
||||
}
|
||||
|
||||
export -f cleaninput getPrefix getTodo getNewtodo shellquote filtercommand _list listWordsWithSigil getPadding _format die
|
||||
hasCustomAction()
|
||||
{
|
||||
[ -d "${1:?}" ] || return 1
|
||||
[ -x "$1/${2:?}" ] && return 0
|
||||
if [ -h "$1/$2" ] && [ ! -e "$1/$2" ]
|
||||
then
|
||||
dieWithHelp "$2" "Fatal Error: Broken link to custom action: '$1/$2'"
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
export -f cleaninput getPrefix getTodo getNewtodo filtercommand _list listWordsWithSigil getPadding _format die
|
||||
|
||||
# == HANDLE ACTION ==
|
||||
action=$( printf "%s\n" "$ACTION" | tr '[:upper:]' '[:lower:]' )
|
||||
@@ -1060,11 +1075,11 @@ then
|
||||
shift
|
||||
## Reset action to new first argument
|
||||
action=$( printf "%s\n" "$1" | tr '[:upper:]' '[:lower:]' )
|
||||
elif [ -d "$TODO_ACTIONS_DIR/$action" ] && [ -x "$TODO_ACTIONS_DIR/$action/$action" ]
|
||||
elif hasCustomAction "$TODO_ACTIONS_DIR/$action" "$action"
|
||||
then
|
||||
"$TODO_ACTIONS_DIR/$action/$action" "$@"
|
||||
exit $?
|
||||
elif [ -d "$TODO_ACTIONS_DIR" ] && [ -x "$TODO_ACTIONS_DIR/$action" ]
|
||||
elif hasCustomAction "$TODO_ACTIONS_DIR" "$action"
|
||||
then
|
||||
"$TODO_ACTIONS_DIR/$action" "$@"
|
||||
exit $?
|
||||
@@ -1074,8 +1089,7 @@ fi
|
||||
case $action in
|
||||
"add" | "a")
|
||||
if [[ -z "$2" && $TODOTXT_FORCE = 0 ]]; then
|
||||
echo -n "Add: "
|
||||
read -e -r input
|
||||
read -p "Add: " -e -r input
|
||||
else
|
||||
[ -z "$2" ] && die "usage: $TODO_SH add \"TODO ITEM\""
|
||||
shift
|
||||
@@ -1086,8 +1100,7 @@ case $action in
|
||||
|
||||
"addm")
|
||||
if [[ -z "$2" && $TODOTXT_FORCE = 0 ]]; then
|
||||
echo -n "Add: "
|
||||
read -e -r input
|
||||
read -p "Add: " -e -r input
|
||||
else
|
||||
[ -z "$2" ] && die "usage: $TODO_SH addm \"TODO ITEM\""
|
||||
shift
|
||||
@@ -1127,8 +1140,7 @@ case $action in
|
||||
getTodo "$item"
|
||||
|
||||
if [[ -z "$1" && $TODOTXT_FORCE = 0 ]]; then
|
||||
echo -n "Append: "
|
||||
read -e -r input
|
||||
read -p "Append: " -e -r input
|
||||
else
|
||||
input=$*
|
||||
fi
|
||||
@@ -1179,7 +1191,7 @@ case $action in
|
||||
echo "TODO: $item deleted."
|
||||
fi
|
||||
else
|
||||
echo "TODO: No tasks were deleted."
|
||||
die "TODO: No tasks were deleted."
|
||||
fi
|
||||
else
|
||||
sed -i.bak \
|
||||
@@ -1209,6 +1221,7 @@ case $action in
|
||||
|
||||
# Split multiple depri's, if comma separated change to whitespace separated
|
||||
# Loop the 'depri' function for each item
|
||||
status=0
|
||||
for item in ${*//,/ }; do
|
||||
getTodo "$item"
|
||||
|
||||
@@ -1220,9 +1233,11 @@ case $action in
|
||||
echo "TODO: $item deprioritized."
|
||||
fi
|
||||
else
|
||||
echo "TODO: $item is not prioritized."
|
||||
echo >&2 "TODO: $item is not prioritized."
|
||||
status=1
|
||||
fi
|
||||
done
|
||||
exit $status
|
||||
;;
|
||||
|
||||
"do" | "done" )
|
||||
@@ -1233,6 +1248,7 @@ case $action in
|
||||
|
||||
# Split multiple do's, if comma separated change to whitespace separated
|
||||
# Loop the 'do' function for each item
|
||||
status=0
|
||||
for item in ${*//,/ }; do
|
||||
getTodo "$item"
|
||||
|
||||
@@ -1248,15 +1264,17 @@ case $action in
|
||||
echo "TODO: $item marked as done."
|
||||
fi
|
||||
else
|
||||
echo "TODO: $item is already marked done."
|
||||
echo >&2 "TODO: $item is already marked done."
|
||||
status=1
|
||||
fi
|
||||
done
|
||||
|
||||
if [ $TODOTXT_AUTO_ARCHIVE = 1 ]; then
|
||||
# Recursively invoke the script to allow overriding of the archive
|
||||
# action.
|
||||
"$TODO_FULL_SH" archive
|
||||
"$TODO_FULL_SH" archive || status=$?
|
||||
fi
|
||||
exit $status
|
||||
;;
|
||||
|
||||
"help" )
|
||||
@@ -1336,7 +1354,7 @@ case $action in
|
||||
"listpri" | "lsp" )
|
||||
shift ## was "listpri", new $1 is priority to list or first TERM
|
||||
|
||||
pri=$(printf "%s\n" "$1" | tr '[:lower:]' '[:upper:]' | grep -e '^[A-Z]$' -e '^[A-Z]-[A-Z]$') && shift || pri="A-Z"
|
||||
pri=$(set -o pipefail; printf "%s\n" "$1" | grep '^\([A-Za-z]\|[A-Za-z]-[A-Za-z]\|[A-Z][A-Z-]*[A-Z]\)$' | tr '[:lower:]' '[:upper:]') && shift || pri="A-Z"
|
||||
post_filter_command="${post_filter_command:-}${post_filter_command:+ | }grep '^ *[0-9]\+ ([${pri}]) '"
|
||||
_list "$TODO_FILE" "$@"
|
||||
;;
|
||||
@@ -1372,7 +1390,7 @@ case $action in
|
||||
echo "TODO: $item moved from '$src' to '$dest'."
|
||||
fi
|
||||
else
|
||||
echo "TODO: No tasks moved."
|
||||
die "TODO: No tasks moved."
|
||||
fi
|
||||
;;
|
||||
|
||||
@@ -1383,6 +1401,7 @@ case $action in
|
||||
|
||||
"pri" | "p" )
|
||||
shift
|
||||
status=0
|
||||
while [ "$#" -gt 0 ] ; do
|
||||
item=$1
|
||||
newpri=$( printf "%s\n" "$2" | tr '[:lower:]' '[:upper:]' )
|
||||
@@ -1414,10 +1433,12 @@ note: PRIORITY must be anywhere from A to Z."
|
||||
fi
|
||||
fi
|
||||
if [ "$oldpri" = "$newpri" ]; then
|
||||
echo "TODO: $item already prioritized ($newpri)."
|
||||
echo >&2 "TODO: $item already prioritized ($newpri)."
|
||||
status=1
|
||||
fi
|
||||
shift; shift
|
||||
done
|
||||
exit $status
|
||||
;;
|
||||
|
||||
"replace" )
|
||||
@@ -1485,7 +1506,7 @@ note: PRIORITY must be anywhere from A to Z."
|
||||
newTaskNum=$( sed -e '/./!d' "$TODO_FILE" | sed -n '$ =' )
|
||||
deduplicateNum=$(( originalTaskNum - newTaskNum ))
|
||||
if [ $deduplicateNum -eq 0 ]; then
|
||||
echo "TODO: No duplicate tasks found"
|
||||
die "TODO: No duplicate tasks found"
|
||||
else
|
||||
echo "TODO: $deduplicateNum duplicate task(s) removed"
|
||||
fi
|
||||
|
||||
@@ -18,7 +18,7 @@ _todo()
|
||||
mv prepend prep pri p replace report shorthelp"
|
||||
local -r MOVE_COMMAND_PATTERN='move|mv'
|
||||
|
||||
local _todo_sh=${_todo_sh:-todo.sh}
|
||||
local _todo_sh=${_todo_sh:-${COMP_WORDS[0]}}
|
||||
local completions
|
||||
if [ "$COMP_CWORD" -eq 1 ]; then
|
||||
completions="$COMMANDS $(eval TODOTXT_VERBOSE=0 $_todo_sh command listaddons 2>/dev/null) $OPTS"
|
||||
@@ -101,22 +101,14 @@ complete -F _todo todo.sh
|
||||
# ~/.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
|
||||
# PATH, you need to add and use a wrapper completion function, like this:
|
||||
#_todoElsewhere()
|
||||
# The completion uses the alias itself, so any custom arguments (like a custom
|
||||
# configuration (-d "$HOME/todo2.cfg")) are used there as well.
|
||||
# If you don't want this, or need to further tweak the todo.sh command that's
|
||||
# used by the completion, you can add and use a wrapper completion function that
|
||||
# redefines _todo_sh before invoking _todo():
|
||||
#_todo_tweak()
|
||||
#{
|
||||
# local _todo_sh='/path/to/todo2.sh'
|
||||
# local _todo_sh='todo.sh -d "$HOME/todo-tweaked.cfg"'
|
||||
# _todo "$@"
|
||||
#}
|
||||
#complete -F _todoElsewhere /path/to/todo2.sh
|
||||
|
||||
# If you use aliases to use different configuration(s), you need to add and use
|
||||
# a wrapper completion function for each configuration if you want to complete
|
||||
# from the actual configured task locations:
|
||||
#alias todo2='todo.sh -d "$HOME/todo2.cfg"'
|
||||
#_todo2()
|
||||
#{
|
||||
# local _todo_sh='todo.sh -d "$HOME/todo2.cfg"'
|
||||
# _todo "$@"
|
||||
#}
|
||||
#complete -F _todo2 todo2
|
||||
#complete -F _todo_tweak todo.sh
|
||||
|
||||
Reference in New Issue
Block a user