Add files for bash completion
This commit is contained in:
parent
50175a9698
commit
dc73b55b7b
28
shell-completion/Makefile
Normal file
28
shell-completion/Makefile
Normal 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
|
||||||
10
shell-completion/foreach2.m4
Normal file
10
shell-completion/foreach2.m4
Normal 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
|
||||||
94
shell-completion/hledger-completion.bash.m4
Normal file
94
shell-completion/hledger-completion.bash.m4
Normal 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
|
||||||
|
')
|
||||||
65
shell-completion/hledger-completion.sh
Normal file
65
shell-completion/hledger-completion.sh
Normal 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
|
||||||
21
shell-completion/output-commands.sh
Executable file
21
shell-completion/output-commands.sh
Executable 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 "$@"
|
||||||
17
shell-completion/output-options.sh
Executable file
17
shell-completion/output-options.sh
Executable 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 "$@"
|
||||||
9
shell-completion/quote.m4
Normal file
9
shell-completion/quote.m4
Normal 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
|
||||||
Loading…
Reference in New Issue
Block a user