From 177432ec9433b226393443d0fdf571b2f61f1a6f Mon Sep 17 00:00:00 2001 From: Ingo Karkat Date: Tue, 31 Mar 2020 00:44:50 +0200 Subject: [PATCH] ENH: Enable file completion for add-on actions via _todo_file{1,2,3}_actions (#270) * Refactoring: Use regular expression match instead of case globbing for actions taking a SRC argument Making it consistent with the test for MOVE_COMMAND_PATTERN, and allowing to extend the pattern with custom actions in the future. * Refactoring: Move the anchoring and grouping out of MOVE_COMMAND_PATTERN So that additional (custom) add-on actions can be configured in the future. * ENH: Enable file completion for add-on actions via _todo_file{1,2,3}_actions Allowing completion of todo files directly after the add-on action (1), with one (2) / two (3) arguments in between. This should handle most cases. In order to configure the add-on completion, the corresponding configuration variable has to be defined in the user's shell (e.g. via ~/.bashrc): _todo_file1_actions='myaction|anotheraction' --- tests/t6060-completion-addon-files.sh | 28 +++++++ todo_completion | 106 +++++++++++++------------- 2 files changed, 82 insertions(+), 52 deletions(-) create mode 100755 tests/t6060-completion-addon-files.sh diff --git a/tests/t6060-completion-addon-files.sh b/tests/t6060-completion-addon-files.sh new file mode 100755 index 00000000..94ad60ef --- /dev/null +++ b/tests/t6060-completion-addon-files.sh @@ -0,0 +1,28 @@ +#!/bin/bash +# + +test_description='Bash add-on action file completion functionality + +This test checks todo_completion of files for add-on actions that have file argument(s) configured +' +. ./test-lib.sh + +readonly FILES='done.txt report.txt todo.txt' +test_todo_completion 'nothing after unconfigured bar' 'todo.sh bar ' '' + +_todo_file1_actions='foo|bar' +test_todo_completion 'all files after configured bar' 'todo.sh bar ' "$FILES" +test_todo_completion 'nothing after configured bar ITEM#' 'todo.sh bar 1 ' '' + +_todo_file2_actions='baz' +test_todo_completion 'nothing after configured baz' 'todo.sh baz ' '' +test_todo_completion 'all files after configured baz ITEM#' 'todo.sh baz 1 ' "$FILES" +test_todo_completion 'nothing after configured baz ITEM# MORE' 'todo.sh baz 1 more ' '' + +_todo_file3_actions='biz' +test_todo_completion 'nothing after configured biz' 'todo.sh biz ' '' +test_todo_completion 'nothing after configured biz ITEM#' 'todo.sh biz 1 ' '' +test_todo_completion 'all files after configured biz ITEM# MORE' 'todo.sh biz 1 more ' "$FILES" +test_todo_completion 'nothing after configured biz ITEM# EVEN MORE' 'todo.sh biz 1 even more ' '' + +test_done diff --git a/todo_completion b/todo_completion index f26497b0..b403a551 100755 --- a/todo_completion +++ b/todo_completion @@ -14,15 +14,15 @@ _todo() 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" - local -r MOVE_COMMAND_PATTERN='^(move|mv)$' + local -r MOVE_COMMAND_PATTERN='move|mv' local _todo_sh=${_todo_sh:-todo.sh} local completions if [ $COMP_CWORD -eq 1 ]; then completions="$COMMANDS $(eval TODOTXT_VERBOSE=0 $_todo_sh command listaddons 2>/dev/null) $OPTS" elif [[ $COMP_CWORD -gt 2 && ( \ - "${COMP_WORDS[COMP_CWORD-2]}" =~ $MOVE_COMMAND_PATTERN || \ - "${COMP_WORDS[COMP_CWORD-3]}" =~ $MOVE_COMMAND_PATTERN ) ]]; then + "${COMP_WORDS[COMP_CWORD-2]}" =~ ^($MOVE_COMMAND_PATTERN${_todo_file2_actions:+|${_todo_file2_actions}})$ || \ + "${COMP_WORDS[COMP_CWORD-3]}" =~ ^($MOVE_COMMAND_PATTERN${_todo_file3_actions:+|${_todo_file3_actions}})$ ) ]]; then # "move ITEM# DEST [SRC]" has file arguments on positions 2 and 3. completions=$(eval TODOTXT_VERBOSE=0 $_todo_sh command listfile 2>/dev/null) else @@ -31,56 +31,58 @@ _todo() completions=$COMMANDS;; help) completions="$COMMANDS $(eval TODOTXT_VERBOSE=0 $_todo_sh command listaddons 2>/dev/null)";; - addto|listfile|lf) - completions=$(eval TODOTXT_VERBOSE=0 $_todo_sh command listfile 2>/dev/null);; -*) completions="$COMMANDS $(eval TODOTXT_VERBOSE=0 $_todo_sh command listaddons 2>/dev/null) $OPTS";; - *) case "$cur" in - +*) completions=$(eval TODOTXT_VERBOSE=0 $_todo_sh command listproj 2>/dev/null) - COMPREPLY=( $( compgen -W "$completions" -- $cur )) - [ ${#COMPREPLY[@]} -gt 0 ] && return 0 - # Fall back to projects extracted from done tasks. - completions=$(eval 'TODOTXT_VERBOSE=0 TODOTXT_SOURCEVAR=\$DONE_FILE' $_todo_sh command listproj 2>/dev/null) - ;; - @*) completions=$(eval TODOTXT_VERBOSE=0 $_todo_sh command listcon 2>/dev/null) - COMPREPLY=( $( compgen -W "$completions" -- $cur )) - [ ${#COMPREPLY[@]} -gt 0 ] && return 0 - # Fall back to contexts extracted from done tasks. - completions=$(eval 'TODOTXT_VERBOSE=0 TODOTXT_SOURCEVAR=\$DONE_FILE' $_todo_sh command listcon 2>/dev/null) - ;; - *) if [[ "$cur" =~ ^[0-9]+$ ]]; then - declare -a sedTransformations=( - # Remove the (padded) task number; we prepend the - # user-provided $cur instead. - -e 's/^ *[0-9]\{1,\} //' - # Remove the timestamp prepended by the -t option, - # but keep any priority (as it's short and may - # provide useful context). - -e 's/^\((.) \)\{0,1\}[0-9]\{2,4\}-[0-9]\{2\}-[0-9]\{2\} /\1/' - # Remove the done date and (if there) the timestamp. - # Keep the "x" (as it's short and may provide useful - # context) - -e 's/^\([xX] \)\([0-9]\{2,4\}-[0-9]\{2\}-[0-9]\{2\} \)\{1,2\}/\1/' - # Remove any trailing whitespace; the Bash - # completion inserts a trailing space itself. - -e 's/[[:space:]]*$//' - # Finally, limit the output to a single line just as - # a safety check of the ls action output. - -e '1q' - ) - local todo=$( \ - eval TODOTXT_VERBOSE=0 $_todo_sh '-@ -+ -p -x command ls "^ *${cur} "' 2>/dev/null | \ - sed "${sedTransformations[@]}" \ - ) - # Append task text as a shell comment. This - # completion can be a safety check before a - # destructive todo.txt operation. - [ "$todo" ] && COMPREPLY[0]="$cur # $todo" - return 0 - else - return 0 - fi - ;; - esac + *) if [[ "$prev" =~ ^(addto|listfile|lf${_todo_file1_actions:+|${_todo_file1_actions}})$ ]]; then + completions=$(eval TODOTXT_VERBOSE=0 $_todo_sh command listfile 2>/dev/null) + else + case "$cur" in + +*) completions=$(eval TODOTXT_VERBOSE=0 $_todo_sh command listproj 2>/dev/null) + COMPREPLY=( $( compgen -W "$completions" -- $cur )) + [ ${#COMPREPLY[@]} -gt 0 ] && return 0 + # Fall back to projects extracted from done tasks. + completions=$(eval 'TODOTXT_VERBOSE=0 TODOTXT_SOURCEVAR=\$DONE_FILE' $_todo_sh command listproj 2>/dev/null) + ;; + @*) completions=$(eval TODOTXT_VERBOSE=0 $_todo_sh command listcon 2>/dev/null) + COMPREPLY=( $( compgen -W "$completions" -- $cur )) + [ ${#COMPREPLY[@]} -gt 0 ] && return 0 + # Fall back to contexts extracted from done tasks. + completions=$(eval 'TODOTXT_VERBOSE=0 TODOTXT_SOURCEVAR=\$DONE_FILE' $_todo_sh command listcon 2>/dev/null) + ;; + *) if [[ "$cur" =~ ^[0-9]+$ ]]; then + declare -a sedTransformations=( + # Remove the (padded) task number; we prepend the + # user-provided $cur instead. + -e 's/^ *[0-9]\{1,\} //' + # Remove the timestamp prepended by the -t option, + # but keep any priority (as it's short and may + # provide useful context). + -e 's/^\((.) \)\{0,1\}[0-9]\{2,4\}-[0-9]\{2\}-[0-9]\{2\} /\1/' + # Remove the done date and (if there) the timestamp. + # Keep the "x" (as it's short and may provide useful + # context) + -e 's/^\([xX] \)\([0-9]\{2,4\}-[0-9]\{2\}-[0-9]\{2\} \)\{1,2\}/\1/' + # Remove any trailing whitespace; the Bash + # completion inserts a trailing space itself. + -e 's/[[:space:]]*$//' + # Finally, limit the output to a single line just as + # a safety check of the ls action output. + -e '1q' + ) + local todo=$( \ + eval TODOTXT_VERBOSE=0 $_todo_sh '-@ -+ -p -x command ls "^ *${cur} "' 2>/dev/null | \ + sed "${sedTransformations[@]}" \ + ) + # Append task text as a shell comment. This + # completion can be a safety check before a + # destructive todo.txt operation. + [ "$todo" ] && COMPREPLY[0]="$cur # $todo" + return 0 + else + return 0 + fi + ;; + esac + fi ;; esac fi