Return from user prompt without requiring Enter (#354)

* Tests: Add coverage for del / move without -f, but with prompting

Supplying the user confirmation via "yes".

* Cosmetics: Align inconsistent spacing for before (y/n) prompt

* Refactoring: Extract confirm() function

The user confirmation query had been duplicated (once) in the code.

* Refactoring: confirm(): Leave early if forced

* Return from user prompt without requiring Enter

By just reading a single character (y for yes, anything else: no).



* Tests: Ensure that only a single "y" concludes the confirmation

By switching from "yes" (that endlessly prints newline-separated "y"s) to "printf y".

* t1800-del: Add coverage for negative confirmation

Negative means "anything but y", so "n", "x", and Enter all apply.

* Cosmetics: Add trailing space after (y/n) prompt

So that the user's typed answer is not recorded directly after it, but with separation: "Foo? (y/n) y" instead of "Foo? (y/n)y".

*Compatibility: "read -N 1" is only available in Bash 4.1+

Mac OS still ships with Bash 3.2 :-( Fall back to the original prompting that requires conclusion via Enter then.
Note: Even though the tests use "printf y", this still gets accepted, as there'll be EOF after that. In real use (when stdin from the terminal stays open), a concluding Enter is mandatory, though.

Closes #152
This commit is contained in:
Ingo Karkat
2021-08-06 22:05:11 +02:00
committed by GitHub
parent 587833bb4e
commit ee94a3fac5
3 changed files with 91 additions and 14 deletions

30
todo.sh
View File

@@ -362,6 +362,20 @@ die()
exit 1
}
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
echo
[ "$answer" = "y" ]
}
cleaninput()
{
# Parameters: When $1 = "for sed", performs additional escaping for use
@@ -1146,13 +1160,7 @@ case $action in
getTodo "$item"
if [ -z "$3" ]; then
if [ $TODOTXT_FORCE = 0 ]; then
echo "Delete '$todo'? (y/n)"
read -e -r ANSWER
else
ANSWER="y"
fi
if [ "$ANSWER" = "y" ]; then
if confirm "Delete '$todo'"; then
if [ $TODOTXT_PRESERVE_LINE_NUMBERS = 0 ]; then
# delete line (changes line numbers)
sed -i.bak -e "${item}s/^.*//" -e '/./!d' "$TODO_FILE"
@@ -1342,13 +1350,7 @@ case $action in
getTodo "$item" "$src"
[ -z "$todo" ] && die "$item: No such item in $src."
if [ $TODOTXT_FORCE = 0 ]; then
echo "Move '$todo' from $src to $dest? (y/n)"
read -e -r ANSWER
else
ANSWER="y"
fi
if [ "$ANSWER" = "y" ]; then
if confirm "Move '$todo' from $src to $dest"; then
if [ $TODOTXT_PRESERVE_LINE_NUMBERS = 0 ]; then
# delete line (changes line numbers)
sed -i.bak -e "${item}s/^.*//" -e '/./!d' "$src"