Add files for bash completion

This commit is contained in:
Jakob Schöttl 2019-01-10 19:08:46 +01:00 committed by Simon Michael
parent 50175a9698
commit dc73b55b7b
7 changed files with 244 additions and 0 deletions

28
shell-completion/Makefile Normal file
View File

@ -0,0 +1,28 @@
.PHONY: commands clean
all: generic-options.txt commands hledger-completion.bash
generic-options.txt:
hledger -h | ./output-options.sh > $@
commands.txt:
hledger | ./output-commands.sh > $@
commands-list.txt: commands.txt
paste -sd, $^ | tr -d '\n' > $@
commands: commands.txt
#parallel 'touch {}.command' < commands.txt
parallel 'hledger {} -h | ./output-options.sh > options-{}.txt' < commands.txt
# It's possible to call this rule explicitly but it's not invoked automatically.
# Better generate *-options.txt with the 'commands' phony rule.
%-options.txt: %.command
hledger $* -h | ./output-options.sh > $@
hledger-completion.bash: hledger-completion.bash.m4 commands-list.txt
m4 hledger-completion.bash.m4 > $@
clean:
rm -f *.commands *.txt hledger-completion.bash

View File

@ -0,0 +1,10 @@
include(`quote.m4')dnl
divert(`-1')
# foreach(x, (item_1, item_2, ..., item_n), stmt)
# parenthesized list, improved version
define(`foreach', `pushdef(`$1')_$0(`$1',
(dquote(dquote_elt$2)), `$3')popdef(`$1')')
define(`_arg1', `$1')
define(`_foreach', `ifelse(`$2', `(`')', `',
`define(`$1', _arg1$2)$3`'$0(`$1', (dquote(shift$2)), `$3')')')
divert`'dnl

View File

@ -0,0 +1,94 @@
#!/bin/bash
# Completion script for hledger.
# Created using a Makefile and real hledger.
# No set -e because this file is sourced and is not supposed to quit the current shell.
set -o pipefail
# TODO grep "^$wordToComplete" is not safe to use if the word contains regex
# special chars. But it might be no problem because of COMP_WORDBREAKS.
# TODO Try to get file from -f --file arguments from COMP_WORDS and pass it to
# the 'hledger accounts' call.
# Working with bash arrays is nasty compared to editing a text file. Consider
# for example grepping an array or map a substitution on it.
# Therefore, we create temp files in RAM for completion suggestions (see below).
declare -g HLEDGER_COMPLETION_TEMPDIR
HLEDGER_COMPLETION_TEMPDIR=$(mktemp -d)
hledgerCompletionFunction() {
declare cmd=$1
declare wordToComplete=$2
declare precedingWord=$3
declare subcommand
for subcommand in "${COMP_WORDS[@]}"; do
if grep -Fxqe "$subcommand" "$HLEDGER_COMPLETION_TEMPDIR/commands.txt"; then
#declare -a options
#readarray -t options <(grep "^$wordToComplete" "$HLEDGER_COMPLETION_TEMPDIR/options-$subcommand.txt")
#COMPREPLY+=( "${options[@]}" )
COMPREPLY+=( $(grep -h "^$wordToComplete" -- "$HLEDGER_COMPLETION_TEMPDIR/options-$subcommand.txt") )
break
fi
subcommand=
done
if [[ -z $subcommand ]]; then
declare completeFiles filenameSoFar
case $wordToComplete in
--file=*|--rules-file=*)
completeFiles=1
filenameSoFar=${wordToComplete#*=}
;;
esac
case $precedingWord in
-f|--file|--rules-file)
completeFiles=1
filenameSoFar=$wordToComplete
;;
esac
if [[ -n $completeFiles ]]; then
:
#COMP_WORDBREAKS='= '
COMPREPLY+=( $(compgen -df | grep "^$filenameSoFar") )
else
COMPREPLY+=( $(grep -h "^$wordToComplete" -- "$HLEDGER_COMPLETION_TEMPDIR/commands.txt" "$HLEDGER_COMPLETION_TEMPDIR/generic-options.txt") )
fi
else
# Almost all subcommands accpt [QUERY] -> always add accounts to completion list
COMP_WORDBREAKS=' '
COMPREPLY+=( $(hledger accounts --flat | grep "^$wordToComplete") )
fi
}
complete -F hledgerCompletionFunction hledger
# Include lists of commands and options generated by the Makefile using m4
# macro processor.
# Included files must have exactly one newline at EOF to prevent weired errors.
cat <<TEXT > "$HLEDGER_COMPLETION_TEMPDIR/commands.txt"
include(`commands.txt')dnl
TEXT
cat <<TEXT > "$HLEDGER_COMPLETION_TEMPDIR/generic-options.txt"
include(`generic-options.txt')dnl
TEXT
include(`foreach2.m4')
foreach(`cmd', (include(`commands-list.txt')), `
cat <<TEXT > "$HLEDGER_COMPLETION_TEMPDIR/options-cmd.txt"
include(options-cmd.txt)dnl
TEXT
')

View File

@ -0,0 +1,65 @@
#!/bin/bash
# Completion script for hledger.
# Created using a Makefile and real hledger.
#set -eo pipefail
completeFunction() {
declare cmd=$1
declare wordToComplete=$2
declare precedingWord=$3
declare subcommand
for subcommand in "${COMP_WORDS[@]}"; do
if grep -Fxq "$subcommand" commands.txt; then
#declare -a options
#readarray -t options <(grep "^$wordToComplete" "$subcommand-options.txt")
#COMPREPLY+=( "${options[@]}" )
COMPREPLY+=( $(cat "$subcommand-options.txt" | grep "^$wordToComplete") )
break
fi
subcommand=
done
if [[ -z $subcommand ]]; then
# echo;echo no subcommand
case $precedingWord in
-f|--file|--rules-file)
# COMPREPLY+=( $(compgen -df | grep "^$wordToComplete") )
:
;;
*)
# echo "completing sub commands and general options"
COMPREPLY+=( $(cat commands.txt generic-options.txt | grep "^$wordToComplete") )
;;
esac
else
:
# echo;echo subcommand is $subcommand
# if grep -Eqv '\b(register|reg|r)\b' <<< "$COMP_LINE"; then
# return
# fi
# case $precedingWord in
# register|reg|r) : ;;
# *) return 1 ;;
# esac
declare journalFile
# TODO try to get file from -f --file first
if [[ -n $HLEDGER_FILE ]]; then
journalFile=$HLEDGER_FILE
else
journalFile=~/.hledger.journal
fi
COMP_WORDBREAKS=' '
COMPREPLY+=( $(sed -rn 's/^ +([-_:a-zA-Z0-9]+).*/\1/p' "$journalFile" | grep "^$wordToComplete") )
fi
}
complete -F completeFunction hledger

View File

@ -0,0 +1,21 @@
#!/bin/bash
# Output subcommands from man/usage text
set -o errexit -o pipefail -o nounset
printCommands() {
declare tmp=$1
sed -rn 's/^ ([-a-z]+).*/\1/gp' "$tmp"
sed -rn 's/^ .*\(([a-z]+)\).*/\1/gp' "$tmp"
# TODO missing: (reg, r) (multiple aliases)
}
main() {
declare tmp
tmp=$(mktemp)
cat > "$tmp"
printCommands "$tmp" | grep -v ^hledger
}
main "$@"

View File

@ -0,0 +1,17 @@
#!/bin/bash
# Output short and long options from man/usage text
set -o errexit -o pipefail -o nounset
main() {
declare tmp
tmp=$(mktemp)
cat > "$tmp"
sed -rn 's/.* (-[a-zA-Z0-9]).*/\1/gp' < "$tmp"
# Do not print '=' after long options with arg because it makes completion
# for option arguments harder.
sed -rn 's/.* (--[-a-zA-Z0-9]+)=?.*/\1/gp' < "$tmp"
}
main "$@"

View File

@ -0,0 +1,9 @@
divert(`-1')
# quote(args) - convert args to single-quoted string
define(`quote', `ifelse(`$#', `0', `', ``$*'')')
# dquote(args) - convert args to quoted list of quoted strings
define(`dquote', ``$@'')
# dquote_elt(args) - convert args to list of double-quoted strings
define(`dquote_elt', `ifelse(`$#', `0', `', `$#', `1', ```$1''',
```$1'',$0(shift($@))')')
divert`'dnl