diff --git a/shell-completion/.gitignore b/shell-completion/.gitignore index 2211df63d..d1772c642 100644 --- a/shell-completion/.gitignore +++ b/shell-completion/.gitignore @@ -1 +1,3 @@ -*.txt +options*.txt +generic-options.txt +commands*.txt diff --git a/shell-completion/BSDmakefile b/shell-completion/BSDmakefile new file mode 100644 index 000000000..a21d418e0 --- /dev/null +++ b/shell-completion/BSDmakefile @@ -0,0 +1,4 @@ +.DONE: + @echo "GNU Make (gmake) required to build" +.DEFAULT: + @echo "GNU Make (gmake) required to build" diff --git a/shell-completion/Makefile b/shell-completion/Makefile index fd1866a28..257e34297 100644 --- a/shell-completion/Makefile +++ b/shell-completion/Makefile @@ -1,30 +1,87 @@ +# Setting the number of job runners like this in the makefile only works in +# GNU Make 4.3 or later. Older versions will require that either an env +# variable be set before running or command line flag be passed at runtime to +# get parallel jobs. +MAKEFLAGS += --jobs=$(shell nproc 2>/dev/null || printf 8) -.PHONY: command-options clean +EUID := $(shell id -u) -all: command-options hledger-completion.bash +ifeq ($(EUID),0) +PREFIX := /usr/local +endif -hledger-completion.bash: hledger-completion.bash.m4 commands-list.txt query-filters.txt generic-options.txt - m4 hledger-completion.bash.m4 > $@ +ifdef PREFIX +BASHCOMPDIR := $(PREFIX)/share/bash-completion/completions +else +XDG_DATA_HOME ?= $(HOME)/.local/share +BASH_COMPLETION_USER_DIR ?= $(XDG_DATA_HOME)/bash-completion +BASHCOMPDIR := $(BASH_COMPLETION_USER_DIR)/completions +endif -generic-options.txt: - hledger -h | ./output-options.sh | sort -u > $@ +DESTDIR ?= + +PARSE_COMMANDS := ./parse-commands.sh +PARSE_OPTIONS := ./parse-options.sh + +EXTENSIONS := ui web +INSTALLED_EXTENSIONS := $(foreach EXT,$(EXTENSIONS),$(shell type hledger-$(EXT) >/dev/null 2>&1 && echo $(EXT))) + +COMMANDS := $(sort $(shell $(PARSE_COMMANDS)) $(INSTALLED_EXTENSIONS)) + +ifneq ($(.SHELLSTATUS),0) +$(error Error running $(PARSE_COMMANDS)) +endif + +CMDOPTFILES := $(foreach CMD,$(COMMANDS),options-$(CMD).txt) + +define M4DEPS := +hledger-completion.bash.m4 \ +hledger-completion.bash.stub \ +commands.txt \ +commands-list.txt \ +query-filters.txt \ +generic-options.txt \ +$(CMDOPTFILES) +endef + + +all: hledger-completion.bash + +.PHONY: install +install: + @install -v -d "$(DESTDIR)$(BASHCOMPDIR)" + @install -v -m 0644 hledger-completion.bash "$(DESTDIR)$(BASHCOMPDIR)/hledger" + @for ext in $(EXTENSIONS); do \ + printf "symlink " ; \ + ln -sfv hledger "$(DESTDIR)$(BASHCOMPDIR)/hledger-$$ext" ; \ + done + +.PHONY: uninstall +uninstall: + @rm -vf "$(DESTDIR)$(BASHCOMPDIR)/hledger" + @for ext in $(EXTENSIONS); do \ + rm -vf "$(DESTDIR)$(BASHCOMPDIR)/hledger-$$ext" ; \ + done + +hledger-completion.bash: $(M4DEPS) + m4 -g hledger-completion.bash.m4 > $@ commands.txt: - hledger | ./output-commands.sh | grep -v ^hledger > $@ - echo ui >> $@ - echo web >> $@ - echo api >> $@ + printf "%s\n" $(COMMANDS) > $@ -commands-list.txt: commands.txt - paste -sd, $^ | tr -d '\n' > $@ +commands-list.txt: + printf "%s," $(COMMANDS) | sed 's/,$$//' > $@ -#query-filters.txt: - # The query filters are hard to extract! - # hledger help --cat hledger | sed -n '/^QUERIES/,/^[A-Z]/p' +generic-options.txt: + $(PARSE_OPTIONS) > $@ -command-options: commands.txt - parallel -j8 'hledger {} -h | ./output-options.sh | sort -u > options-{}.txt' < commands.txt +options-%.txt: + $(PARSE_OPTIONS) $* > $@ +.PHONY: clean clean: rm -f commands*.txt generic-options.txt options-*.txt + +.PHONY: clean-all +clean-all: clean rm -f hledger-completion.bash diff --git a/shell-completion/README.md b/shell-completion/README.md index 8c9d961ab..26204a517 100644 --- a/shell-completion/README.md +++ b/shell-completion/README.md @@ -15,8 +15,8 @@ The completions can handle hledger's CLI: - commands and generic options - command-specific options -- filenames for options that take a filename as argument -- account names from journal files (but not yet for files named by `--file`) +- most option arguments +- account names, tags, payees, etc. from journal files - query filter keywords like `status:`, `tag:`, or `amt:` Installation for end users @@ -27,22 +27,45 @@ Completions are currently only implemented for the Bash shell. Please check first if the completions for hledger are already installed on your distribution. Refer to the last paragraph of this section for how to test that. -To install the completions manually, follow this steps: - -- Download or copy the file `shell-completion/hledger-completion.bash` and save - it as `~/.hledger-completion.bash`. - -- Add the command `source ~/.hledger-completion.bash` this to the end of your - `~/.bashrc` file. - -- Then, you have to start a new Bash, e.g. by typing `bash` on the current - shell. - -Example installation script: +To install from the repository, do: +```sh +cd /path-to-repo/shell-completion +make install ``` -cp hledger-completion.bash ~/.hledger-completion.bash -echo 'source ~/.hledger-completion.bash' >> ~/.bashrc + +Completions installed this way will be loaded dynamically after you use the hledger +command. Upon the first invocation of a command that has no predefined completion +bash looks for a file with the same name in a set of predefined locations in this order: + +- `$BASH_COMPLETION_USER_DIR/completions` +- `$XDG_DATA_HOME/bash-completion/completions` +- `$HOME/.local/share/bash-completion/completions` +- etc. + +You can manually achieve the effects of `make install` by copying +`shell-completion/hledger-completion.bash` to one of the above, and renaming it +to `hledger`, `_hledger` or `hledger.bash`. For the gory details, type this in a +bash shell: + +```sh +type __load_completion +``` + +To install the completions manually, you can also just download and copy +`shell-completion/hledger-completion.bash` to a directory of your choosing, and +source it from your shell start up files. This way completions are loaded +eagerly and that adds a delay to shell start up time. + +Example: + +```sh +cp hledger-completion.bash ~/.bash_completion.d/hledger +echo 'source ~/.bash_completion.d/hledger' >> ~/.bashrc +# Restart shell +exec bash +# Confirm that completion is loaded +complete -p hledger ``` Now, try it by typing `hledger` (with a space after the command) and press the @@ -63,21 +86,23 @@ Information for developers Generate the completion script for Bash: -``` +```sh # change into this folder: cd shell-completion/ make ``` -Hint: GNU make, GNU m4, and GNU parallel must be installed to call `make`. -The first two usually are. +Hint: GNU make and GNU m4 must be installed to call `make`. +These are present on most systems anyway. +Additionally the build will run a lot faster with parallell jobs. +Use `make -j$(nproc)` for best effect. The generated completion script must be installed. The package maintainer for your distribution should be responsible for this. For now, or to live-test the script, you can use these two commands: -``` +```sh ln -s hledger-completion.bash ~/.hledger-completion.bash echo 'source ~/.hledger-completion.bash' >> ~/.bashrc ``` diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index c4b1dda77..9599adcb7 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -1,2511 +1,2079 @@ +# -*- mode: sh; sh-basic-offset: 4; indent-tabs-mode: nil -*- +# ex: ft=sh ts=4 sw=4 et +# shellcheck disable=2034,2154 # 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 +# This script is sourced by an interactive shell, so do NOT do things like +# 'set -o pipefail' or mangle the global environment in any other way! +# That said, we *do* remove colon (:) from COMP_WORDBREAKS which impacts +# the rest of the session and completion for other programs. -# Note: grep "^$wordToComplete" is (functional) not safe to use if the word -# contains regex special chars. But it might be no problem because of -# COMP_WORDBREAKS. +# INSTALLATION: +# To install you can simply source this file from your shell's startup files. +# +# Alternatively, copy/symlink it into `${BASH_COMPLETION_USER_DIR}/completions` +# or `${XDG_DATA_HOME:-$HOME/.local/share}/bash-completion/completions`, rename +# it to either `hledger`, `_hledger` or `hledger.bash`, and it will be loaded +# dynamically the first time you use the `hledger` command. Optionally, create +# symlinks to this file for any extensions used e.g.: +# +# mkdir -p "${BASH_COMPLETION_USER_DIR:-${XDG_DATA_HOME:-$HOME/.local/share}/bash-completion}/completions" && +# cd "${BASH_COMPLETION_USER_DIR:-${XDG_DATA_HOME:-$HOME/.local/share}/bash-completion}/completions" && +# cp /path/to/hledger-completion.bash hledger && +# ln -s hledger hledger-ui && +# ln -s hledger hledger-web && +# : done. -# Note: compgen and compopt is pretty complicated. Piping to -# grep "^$wordToComplete" -# seems like a hack - I'd rather use -# compgen ... -- "$wordToComplete" -# But what options to use? I don't want to use -W because it may exceed the -# maximum command line length. -C "cat file" is not working either. It would be -# best if compgen could read from stdin but it does not. -# Note: Working with bash arrays is nasty compared to editing a text file. -# Consider for example grepping an array or mapping a substitution on it. -# Therefore, we create temp files in RAM for completion suggestions (see below). +_hledger_completion() { + local cur prev words cword + _init_completion -n : || return 0 -readonly _HLEDGER_COMPLETION_TEMPDIR=$(mktemp -d) + # Current treatment for special characters: + # - exclude colon (:) from COMP_WORDBREAKS + # - option processing assumes that `=` is in COMP_WORDBREAKS + # - use compopt -o filenames selectively to escape the rest + COMP_WORDBREAKS=${COMP_WORDBREAKS//:} + case $COMP_WORDBREAKS in + *=*) : ;; + *) COMP_WORDBREAKS=$COMP_WORDBREAKS= ;; + esac -_hledger_completion_function() { - #declare cmd=$1 - declare wordToComplete=$2 - declare precedingWord=$3 + local subcommand + local subcommandOptions + local i + for ((i=1; i<${#words[@]}; i++)); do + subcommand=${words[i]} + if ! grep -Fxqe "$subcommand" <<< "$_hledger_complist_commands"; then + subcommand= + continue + fi + # There could be other commands begining with $subcommand, e.g.: + # $subcommand == reg --> register, register-match, + # $subcommand == bal --> balance, balancesheet, balancesheetequity, etc. + # Do not ignore them! + if ((i == cword)); then + _hledger_compreply "$( + _hledger_compgen "$_hledger_complist_commands" + )" + return 0 + fi - declare subcommand - for subcommand in "${COMP_WORDS[@]}"; do - if grep -Fxqe "$subcommand" "$_HLEDGER_COMPLETION_TEMPDIR/commands.txt"; then - COMPREPLY+=( $(grep -h "^$wordToComplete" -- "$_HLEDGER_COMPLETION_TEMPDIR/options-$subcommand.txt") ) - break - fi - subcommand= + # Replace dashes with underscores and use indirect expansion + subcommandOptions=_hledger_complist_options_${subcommand//-/_} + + if [[ $cur == -* ]]; then + _hledger_compreply "$(_hledger_compgen "${!subcommandOptions}")" + # Suspend space on completion of long options requiring an argument + [[ ${COMPREPLY[0]} == --*= ]] && compopt -o nospace + + return 0 + fi + break done + # Option argument completion + _hledger_compreply_optarg && return + if [[ -z $subcommand ]]; then + if [[ $cur == -* ]]; then + _hledger_compreply "$( + _hledger_compgen "$_hledger_complist_generic_options" + )" + # Suspend space on completion of long options requiring an argument + [[ ${COMPREPLY[0]} == --*= ]] && compopt -o nospace + else + _hledger_compreply "$( + _hledger_compgen "$_hledger_complist_commands" + )" + fi - declare completeFiles filenameSoFar - case $precedingWord in - -f|--file|--rules-file) - completeFiles=1 - filenameSoFar=$wordToComplete - ;; - =) - completeFiles=1 - filenameSoFar=$wordToComplete - ;; - esac - - if [[ -n $completeFiles ]]; then - #COMP_WORDBREAKS='= ' - declare -a files - # This does not work because assignment to 'files' in the "pipe - # subshell" has no effect! - #compgen -df | grep "^$filenameSoFar" | readarray -t files - - compopt -o filenames -o dirnames - readarray -t files < <(compgen -f -- "$filenameSoFar") - COMPREPLY=( "${files[@]}" ) - - else - COMPREPLY+=( $(grep -h "^$wordToComplete" -- "$_HLEDGER_COMPLETION_TEMPDIR/commands.txt" "$_HLEDGER_COMPLETION_TEMPDIR/generic-options.txt") ) - fi - - else - - # Almost all subcommands accept [QUERY] - # -> always add accounts to completion list - - # TODO Get ledger file from -f --file arguments from COMP_WORDS and pass it to - # the 'hledger accounts' call. Note that --rules-file - if present - must also - # be passed! - - declare -a accounts - readarray -t accounts < <({ cat "$_HLEDGER_COMPLETION_TEMPDIR/query-filters.txt"; hledger accounts --flat; } | grep "^$wordToComplete") - compopt -o nospace - COMPREPLY+=( "${accounts[@]}" ) - # Special characters (e.g. '-', ':') are allowed in account names. - # Account names with spaces must be still be quoted (e.g. '"Expens') - # for completion. Setting COMP_WORDBREAKS='' would not help here! - COMP_WORDBREAKS=' ' - + return 0 fi + # Set this from here on because queries tend to have lots of special chars + # TODO: better handling of special characters + compopt -o filenames + + # Query completion + _hledger_compreply_query && return + + # Subcommand specific + case $subcommand in + # These do not expect or support any query arguments + commodities|check|files|help|import|print-unique|test) + return 0 + ;; + esac + + # Offer query filters and accounts for the rest + _hledger_compreply "$(_hledger_compgen "$_hledger_complist_query_filters")" + if [[ -z $cur ]]; then + _hledger_compreply_append "$( + _hledger_compgen "$(_hledger accounts --flat --depth 1)" + )" + else + _hledger_compreply_append "$( + _hledger_compgen "$(_hledger accounts --flat)" + )" + fi + + # Suspend space on completion of query prefix + # Do not sort, keep accounts and query filters grouped separately + [[ ${COMPREPLY[0]} == *: ]] && compopt -o nospace + compopt -o nosort + + return 0 } -_hledger_extension_completion_function() { - declare cmd=$1 - - # Change parameters and arguments and call the - # normal hledger completion function. - declare extensionName=${cmd#*-} - export -a COMP_WORDS=( "hledger" "$extensionName" "${COMP_WORDS[@]:1}" ) - #echo; echo "debug: ${COMP_WORDS[@]}" - shift - _hledger_completion_function "hledger" "$@" +_hledger_extension_completion() { + local cmd=${1##*/} + local ext=${cmd#hledger-} + # Pretend that hledger is called with the given extension + # as the first argument and call main completion function + COMP_WORDS=("hledger" "$ext" "${COMP_WORDS[@]:1}") + COMP_CWORD=$((COMP_CWORD + 1)) + _hledger_completion "hledger" "${@:1}" } # Register completion function for hledger: -complete -F _hledger_completion_function hledger +complete -F _hledger_completion hledger # Register completion functions for hledger extensions: -complete -F _hledger_extension_completion_function hledger-ui -complete -F _hledger_extension_completion_function hledger-web +complete -F _hledger_extension_completion hledger-ui hledger-web + +# Helpers + +# Comment out when done +_hledger_debug() { + ((HLEDGER_DEBUG)) || return 0 + local var vars=(words) + (($#)) && vars=("$@") + for var in "${vars[@]}"; do + printf '\ndebug: %s\n' "$(declare -p "$var")" >&2 + done +} + +# Stolen from bash-completion +# This function quotes the argument in a way so that readline dequoting +# results in the original argument. This is necessary for at least +# `compgen' which requires its arguments quoted/escaped: +_hledger_quote_by_ref() +{ + printf -v "$2" %q "$1" + + # If result becomes quoted like this: $'string', re-evaluate in order to + # drop the additional quoting. See also: http://www.mail-archive.com/ + # bash-completion-devel@lists.alioth.debian.org/msg01942.html + [[ ${!2} == \$* ]] && eval "$2=${!2}" +} + +# Set the value of COMPREPLY from newline delimited completion candidates +_hledger_compreply() { + local IFS=$'\n' + # shellcheck disable=2206 + COMPREPLY=($1) +} + +# Append the value of COMPREPLY from newline delimited completion candidates +_hledger_compreply_append() { + local IFS=$'\n' + # shellcheck disable=2206 + COMPREPLY+=($1) +} + +# Generate input suitable for _hledger_compreply() from newline delimited +# completion candidates. It doesn't seem there is a way to feed a literal +# word list to compgen -- it will eat your quotes, drink your booze and... +# Completion candidates are quoted accordingly first and then we leave it to +# compgen to deal with readline. +# +# Arguments: +# $1: a newline separated list with completion cadidates +# $2: (optional) a prefix string to add to generated completions +# $3: (optional) a word to match instead of $cur, the default. +# If $match is null and $prefix is defined the match is done against $cur +# stripped of $prefix. If both $prefix and $match are null we match against +# $cur and no prefix is added to completions. +_hledger_compgen() { + local complist=$1 + local prefix=$2 + local match=$3 + local quoted=() + local word + local i=0 + + while IFS= read -r word; do + _hledger_quote_by_ref "$word" word + quoted[i++]=$word + done <<< "$complist" + + if (($# < 3)); then + match=${cur:${#prefix}} + fi + + local IFS=$'\n' + compgen -P "$prefix" -W "${quoted[*]}" -- "$match" +} + +# Try required option argument completion. Set COMPREPLY and return 0 on +# success, 1 if option doesn't require an argument or out of context +_hledger_compreply_optarg() { + local option=${words[cword - 1]} + local match=$cur + local wordlist + + # Match the empty string on --file=, not the equal sign itself + if [[ $cur == = ]]; then + match="" + # Once input is present, cword is incremented so we compensate + elif [[ $prev == = ]]; then + option=${words[cword - 2]} + fi + + [[ $option == -* ]] || return + + case $option in + --alias) + compopt -o nospace -o filenames + _hledger_compreply "$( + _hledger_compgen "$(_hledger accounts --flat)" "" "$match" + )" + ;; + -f|--file|--rules-file|-o|--output-file) + compopt -o filenames + _hledger_compreply "$(compgen -f -- "$match")" + ;; + --pivot) + compopt -o nosort + wordlist="code description note payee" + _hledger_compreply "$(compgen -W "$wordlist" -- "$match")" + _hledger_compreply_append "$( + _hledger_compgen "$(_hledger tags)" "" "$match" + )" + ;; + --value) + wordlist="cost then end now" + _hledger_compreply "$(compgen -W "$wordlist" -- "$match")" + ;; + -X|--exchange) + _hledger_compreply "$( + _hledger_compgen "$(_hledger commodities)" "" "$match" + )" + ;; + --color|--colour) + compopt -o nosort + wordlist="auto always yes never no" + _hledger_compreply "$(compgen -W "$wordlist" -- "$match")" + ;; + -O|--output-format) + wordlist="txt csv json sql" + _hledger_compreply "$(compgen -W "$wordlist" -- "$match")" + ;; + --close-acct|--open-acct) + compopt -o filenames + _hledger_compreply "$( + _hledger_compgen "$(_hledger accounts --flat)" "" "$match" + )" + ;; + --debug) + wordlist="{1..9}" + _hledger_compreply "$(compgen -W "$wordlist" -- "$match")" + ;; + # Argument required, but no handler (yet) + -b|-e|-p) + _hledger_compreply "" + ;; + # Check if an unhandled long option requires an argument + *) + local optionList argRequired + + if [[ -n $subcommandOptions ]]; then + optionList=${!subcommandOptions} + else + optionList=$_hledger_complist_generic_options + fi + + while IFS= read -r argRequired; do + if [[ $argRequired == "$option=" ]]; then + _hledger_compreply "" + return 0 + fi + done <<< "$optionList" + + return 1 + ;; + esac + + return 0 +} + +# Query filter completion through introspection +_hledger_compreply_query() { + [[ $cur =~ .: ]] || return + local query=${cur%%:*}: + local match=${cur#*:} + grep -Fxqe "$query" <<< "$_hledger_complist_query_filters" || return + + local hledgerArgs=() + case $query in + acct:) + if (( ${#match} )); then + hledgerArgs=(accounts --flat) + else + hledgerArgs=(accounts --flat --depth 1) + fi + ;; + code:) hledgerArgs=(codes) ;; + cur:) hledgerArgs=(commodities) ;; + desc:) hledgerArgs=(descriptions) ;; + note:) hledgerArgs=(notes) ;; + payee:) hledgerArgs=(payees) ;; + tag:) hledgerArgs=(tags) ;; + *) + local wordlist + case $query in + amt:) wordlist="< <= > >=" ;; + real:) wordlist="\ 0" ;; + status:) wordlist="\ * !" ;; + *) return 1 ;; + esac + _hledger_compreply "$( + compgen -P "$query" -W "$wordlist" -- "$match" + )" + return 0 + ;; + esac + + _hledger_compreply "$( + _hledger_compgen "$(_hledger "${hledgerArgs[@]}")" "$query" + )" + + return 0 +} + +# Parse the command line so far and fill the array $optarg with the arguments to +# given options. $optarg should be declared by the caller +_hledger_optarg() { + local options=("$@") + local i j offset + optarg=() + + # hledger balance --file ~/ledger _ + # 0 1 2 3 4 + for ((i=1; i < ${#words[@]} - 2; i++)); do + offset=0 + for j in "${!options[@]}"; do + if [[ ${words[i]} == "${options[j]}" ]]; then + if [[ ${words[i+1]} == '=' ]]; then + offset=2 + else + offset=1 + fi + # Pass it through compgen to unescape it + optarg+=("$(compgen -W "${words[i + offset]}")") + fi + done + ((i += offset)) + done +} + +# Get ledger file from -f --file arguments from COMP_WORDS and pass it to the +# 'hledger' call. Note that --rules-file - if present - must also be passed! +# Multiple files are allowed so pass them all in the order of appearance. +_hledger() { + local hledgerArgs=("$@") + local file + local -a optarg + + _hledger_optarg -f --file + for file in "${optarg[@]}"; do + [[ -f $file ]] && hledgerArgs+=(--file "$file") + done + + _hledger_optarg --rules-file + for file in "${optarg[@]}"; do + [[ -f $file ]] && hledgerArgs+=(--rules-file "$file") + done + + # Discard errors. Is there a way to validate files before using them? + hledger "${hledgerArgs[@]}" 2>/dev/null +} # Include lists of commands and options generated by the Makefile using the # m4 macro processor. # Included files must have exactly one newline at EOF to prevent weired errors. -cat < "$_HLEDGER_COMPLETION_TEMPDIR/commands.txt" -add -import -check-dates -check-dupes -close -diff -rewrite -aregister -balancesheet -balancesheetequity -cashflow -incomestatement -roi +read -r -d "" _hledger_complist_commands <<"__TEXT__" accounts activity +add +areg +aregister +bal balance +balancesheet +balancesheetequity +bs +bse +cashflow +cf +check +close codes commodities descriptions +diff files +help +import +incomestatement +is notes payees prices print print-unique +reg register register-match +rewrite +roi stats tags test -help -equity -areg -bs -bse -cf -is ui web -api -TEXT +__TEXT__ -cat < "$_HLEDGER_COMPLETION_TEMPDIR/query-filters.txt" -not: +read -r -d "" _hledger_complist_query_filters <<"__TEXT__" acct: amt: -amt:< -amt:<= -amt:> -amt:>= code: cur: -desc: date: date2: depth: +desc: +inacct: +not: note: payee: real: -real:0 status: -status:! -status:* tag: -inacct: -TEXT +__TEXT__ -cat < "$_HLEDGER_COMPLETION_TEMPDIR/generic-options.txt" ---alias +read -r -d "" _hledger_complist_generic_options <<"__TEXT__" +--alias= --anon --auto ---aux-date ---begin +--begin= --cleared ---colour +--color= --cost --daily --date2 ---debug ---depth +--debug= +--depth= --empty ---end ---exchange ---file +--end= +--exchange= +--file= --forecast --help --ignore-assertions --infer-market-price +--info +--man --market --monthly --pending ---period ---pivot +--period= +--pivot= --quarterly --real ---rules-file ---unmarked ---value ---version ---weekly ---yearly --B --C --D --E --I --M --N --P --Q --R --U --V --W --X --Y --b --e --f --h --p -TEXT - - - - -cat < "$_HLEDGER_COMPLETION_TEMPDIR/options-add.txt" ---alias ---anon ---debug ---file ---help ---ignore-assertions ---no-new-accounts ---pivot ---rules-file ---version --I --f --h -TEXT - -cat < "$_HLEDGER_COMPLETION_TEMPDIR/options-import.txt" ---alias ---anon ---auto ---aux-date ---begin ---catchup ---cleared ---colour ---cost ---daily ---date2 ---debug ---depth ---dry-run ---empty ---end ---exchange ---file ---forecast ---help ---ignore-assertions ---infer-market-price ---market ---monthly ---new ---pending ---period ---pivot ---quarterly ---real ---rules-file ---unmarked ---value ---version ---weekly ---yearly --B --C --D --E --I --M --N --P --Q --R --U --V --W --X --Y --b --e --f --h --p --x -TEXT - -cat < "$_HLEDGER_COMPLETION_TEMPDIR/options-check-dates.txt" ---alias ---anon ---auto ---aux-date ---begin ---cleared ---colour ---cost ---daily ---date2 ---debug ---depth ---empty ---end ---exchange ---file ---forecast ---help ---ignore-assertions ---infer-market-price ---market ---monthly ---pending ---period ---pivot ---quarterly ---real ---rules-file +--rules-file= --strict --unmarked ---value +--value= --version --weekly --yearly --B --C --D --E --I --M --N --P --Q --R --U --V --W --X --Y --b --e --f --h --p -TEXT +__TEXT__ -cat < "$_HLEDGER_COMPLETION_TEMPDIR/options-check-dupes.txt" ---alias +# Dashes are replaced by m4 with underscores to form valid identifiers +# Referenced by indirect expansion of $subcommandOptions + +read -r -d "" _hledger_complist_options_accounts <<"__TEXT__" +--alias= --anon --auto ---aux-date ---begin +--begin= --cleared ---colour +--color= --cost --daily --date2 ---debug ---depth ---empty ---end ---exchange ---file ---forecast ---help ---ignore-assertions ---infer-market-price ---market ---monthly ---pending ---period ---pivot ---quarterly ---real ---rules-file ---unmarked ---value ---version ---weekly ---yearly --B --C --D --E --I --M --N --P --Q --R --U --V --W --X --Y --b --e --f --h --p -TEXT - -cat < "$_HLEDGER_COMPLETION_TEMPDIR/options-close.txt" ---alias ---anon ---auto ---aux-date ---begin ---cleared ---close ---close-acct ---close-desc ---colour ---cost ---daily ---date2 ---debug ---depth ---empty ---end ---exchange ---explicit ---file ---forecast ---help ---ignore-assertions ---infer-market-price ---interleaved ---market ---monthly ---open ---open-acct ---open-desc ---pending ---period ---pivot ---quarterly ---real ---rules-file ---show-costs ---unmarked ---value ---version ---weekly ---x ---yearly --5 --B --C --D --E --I --M --N --P --Q --R --U --V --W --X --Y --b --e --f --h --p --x -TEXT - -cat < "$_HLEDGER_COMPLETION_TEMPDIR/options-diff.txt" ---alias ---anon ---debug ---file ---help ---ignore-assertions ---pivot ---rules-file ---version --I --f --h -TEXT - -cat < "$_HLEDGER_COMPLETION_TEMPDIR/options-rewrite.txt" ---add-posting ---alias ---anon ---auto ---aux-date ---begin ---cleared ---colour ---cost ---daily ---date2 ---debug ---depth ---diff ---empty ---end ---exchange ---file ---forecast ---help ---ignore-assertions ---infer-market-price ---market ---monthly ---pending ---period ---pivot ---quarterly ---real ---rules-file ---unmarked ---value ---version ---weekly ---yearly --1 --2 --B --C --D --E --I --M --N --P --Q --R --U --V --W --X --Y --b --e --f --h --p -TEXT - -cat < "$_HLEDGER_COMPLETION_TEMPDIR/options-aregister.txt" ---alias ---anon ---auto ---aux-date ---begin ---cleared ---colour ---cost ---daily ---date2 ---debug ---depth ---empty ---end ---exchange ---file ---forecast ---help ---ignore-assertions ---infer-market-price ---market ---monthly ---no-elide ---output-file ---output-format ---pending ---period ---pivot ---quarterly ---real ---rules-file ---txn-dates ---unmarked ---value ---version ---weekly ---width ---yearly --B --C --D --E --H --I --M --N --O --P --Q --R --U --V --W --X --Y --b --e --f --h --o --p --w -TEXT - -cat < "$_HLEDGER_COMPLETION_TEMPDIR/options-balancesheet.txt" ---alias ---anon ---auto ---aux-date ---average ---begin ---change ---cleared ---colour ---cost ---cumulative ---daily ---date2 ---debug ---depth ---drop ---empty ---end ---exchange ---file ---flat ---forecast ---format ---help ---historical ---ignore-assertions ---infer-market-price ---market ---monthly ---no-elide ---no-total ---output-file ---output-format ---pending ---percent ---period ---pivot ---pretty-tables ---quarterly ---real ---row-total ---rules-file ---sort-amount ---tree ---unmarked ---value ---version ---weekly ---yearly --A --B --C --D --E --H --I --M --N --O --P --Q --R --S --T --U --V --W --X --Y --b --e --f --h --l --o --p --t -TEXT - -cat < "$_HLEDGER_COMPLETION_TEMPDIR/options-balancesheetequity.txt" ---alias ---anon ---auto ---aux-date ---average ---begin ---change ---cleared ---colour ---cost ---cumulative ---daily ---date2 ---debug ---depth ---drop ---empty ---end ---exchange ---file ---flat ---forecast ---format ---help ---historical ---ignore-assertions ---infer-market-price ---market ---monthly ---no-elide ---no-total ---output-file ---output-format ---pending ---percent ---period ---pivot ---pretty-tables ---quarterly ---real ---row-total ---rules-file ---sort-amount ---tree ---unmarked ---value ---version ---weekly ---yearly --A --B --C --D --E --H --I --M --N --O --P --Q --R --S --T --U --V --W --X --Y --b --e --f --h --l --o --p --t -TEXT - -cat < "$_HLEDGER_COMPLETION_TEMPDIR/options-cashflow.txt" ---alias ---anon ---auto ---aux-date ---average ---begin ---change ---cleared ---colour ---cost ---cumulative ---daily ---date2 ---debug ---depth ---drop ---empty ---end ---exchange ---file ---flat ---forecast ---format ---help ---historical ---ignore-assertions ---infer-market-price ---market ---monthly ---no-elide ---no-total ---output-file ---output-format ---pending ---percent ---period ---pivot ---pretty-tables ---quarterly ---real ---row-total ---rules-file ---sort-amount ---tree ---unmarked ---value ---version ---weekly ---yearly --A --B --C --D --E --H --I --M --N --O --P --Q --R --S --T --U --V --W --X --Y --b --e --f --h --l --o --p --t -TEXT - -cat < "$_HLEDGER_COMPLETION_TEMPDIR/options-incomestatement.txt" ---alias ---anon ---auto ---aux-date ---average ---begin ---change ---cleared ---colour ---cost ---cumulative ---daily ---date2 ---debug ---depth ---drop ---empty ---end ---exchange ---file ---flat ---forecast ---format ---help ---historical ---ignore-assertions ---infer-market-price ---market ---monthly ---no-elide ---no-total ---output-file ---output-format ---pending ---percent ---period ---pivot ---pretty-tables ---quarterly ---real ---row-total ---rules-file ---sort-amount ---tree ---unmarked ---value ---version ---weekly ---yearly --A --B --C --D --E --H --I --M --N --O --P --Q --R --S --T --U --V --W --X --Y --b --e --f --h --l --o --p --t -TEXT - -cat < "$_HLEDGER_COMPLETION_TEMPDIR/options-roi.txt" ---alias ---anon ---auto ---aux-date ---begin ---cashflow ---cleared ---colour ---cost ---daily ---date2 ---debug ---depth ---empty ---end ---exchange ---file ---forecast ---help ---ignore-assertions ---infer-market-price ---inv ---investment ---market ---monthly ---pending ---period ---pivot ---pnl ---quarterly ---real ---rules-file ---unmarked ---value ---version ---weekly ---yearly --B --C --D --E --I --M --N --P --Q --R --U --V --W --X --Y --b --e --f --h --p -TEXT - -cat < "$_HLEDGER_COMPLETION_TEMPDIR/options-accounts.txt" ---alias ---anon ---auto ---aux-date ---begin ---cleared ---colour ---cost ---daily ---date2 ---debug +--debug= --declared ---depth ---drop +--depth= +--drop= --empty ---end ---exchange ---file +--end= +--exchange= +--file= --flat --forecast --help --ignore-assertions --infer-market-price +--info +--man --market --monthly --pending ---period ---pivot +--period= +--pivot= --quarterly --real ---rules-file +--rules-file= +--strict --tree --unmarked --used ---value +--value= --version --weekly --yearly --B --C --D --E --I --M --N --P --Q --R --U --V --W --X --Y --b --e --f --h --l --p --t -TEXT +__TEXT__ -cat < "$_HLEDGER_COMPLETION_TEMPDIR/options-activity.txt" ---alias +read -r -d "" _hledger_complist_options_activity <<"__TEXT__" +--alias= --anon --auto ---aux-date ---begin +--begin= --cleared ---colour +--color= --cost --daily --date2 ---debug ---depth +--debug= +--depth= --empty ---end ---exchange ---file +--end= +--exchange= +--file= --forecast --help --ignore-assertions --infer-market-price +--info +--man --market --monthly --pending ---period ---pivot +--period= +--pivot= --quarterly --real ---rules-file +--rules-file= +--strict --unmarked ---value +--value= --version --weekly --yearly --B --C --D --E --I --M --N --P --Q --R --U --V --W --X --Y --b --e --f --h --p -TEXT +__TEXT__ -cat < "$_HLEDGER_COMPLETION_TEMPDIR/options-balance.txt" ---alias +read -r -d "" _hledger_complist_options_add <<"__TEXT__" +--alias= +--anon +--debug= +--file= +--help +--ignore-assertions +--info +--man +--no-new-accounts +--pivot= +--rules-file= +--strict +--version +__TEXT__ + +read -r -d "" _hledger_complist_options_areg <<"__TEXT__" +--alias= +--anon +--auto +--begin= +--cleared +--color= +--cost +--daily +--date2 +--debug= +--depth= +--empty +--end= +--exchange= +--file= +--forecast +--help +--ignore-assertions +--infer-market-price +--info +--man +--market +--monthly +--no-elide +--output-file= +--output-format= +--pending +--period= +--pivot= +--quarterly +--real +--rules-file= +--strict +--txn-dates +--unmarked +--value= +--version +--weekly +--width= +--yearly +__TEXT__ + +read -r -d "" _hledger_complist_options_aregister <<"__TEXT__" +--alias= +--anon +--auto +--begin= +--cleared +--color= +--cost +--daily +--date2 +--debug= +--depth= +--empty +--end= +--exchange= +--file= +--forecast +--help +--ignore-assertions +--infer-market-price +--info +--man +--market +--monthly +--no-elide +--output-file= +--output-format= +--pending +--period= +--pivot= +--quarterly +--real +--rules-file= +--strict +--txn-dates +--unmarked +--value= +--version +--weekly +--width= +--yearly +__TEXT__ + +read -r -d "" _hledger_complist_options_bal <<"__TEXT__" +--alias= --anon --auto ---aux-date --average ---begin +--begin= --budget --change --cleared ---color ---colour +--color= --cost --cumulative --daily --date2 ---debug ---depth ---drop +--debug= +--depth= +--drop= --empty ---end ---exchange ---file +--end= +--exchange= +--file= --flat --forecast ---format +--format= --help --historical --ignore-assertions --infer-market-price +--info --invert +--man --market --monthly --no-elide --no-total ---output-file ---output-format +--output-file= +--output-format= --pending --percent ---period ---pivot +--period= +--pivot= --pretty-tables --quarterly --real --row-total ---rules-file +--rules-file= --sort-amount +--strict --transpose --tree --unmarked ---value +--value= --version --weekly --yearly --1 --A --B --C --D --E --H --I --M --N --O --P --Q --R --S --T --U --V --W --X --Y --b --e --f --h --l --o --p --t -TEXT +__TEXT__ -cat < "$_HLEDGER_COMPLETION_TEMPDIR/options-codes.txt" ---alias +read -r -d "" _hledger_complist_options_balance <<"__TEXT__" +--alias= --anon --auto ---aux-date ---begin +--average +--begin= +--budget +--change --cleared ---colour +--color= +--cost +--cumulative +--daily +--date2 +--debug= +--depth= +--drop= +--empty +--end= +--exchange= +--file= +--flat +--forecast +--format= +--help +--historical +--ignore-assertions +--infer-market-price +--info +--invert +--man +--market +--monthly +--no-elide +--no-total +--output-file= +--output-format= +--pending +--percent +--period= +--pivot= +--pretty-tables +--quarterly +--real +--row-total +--rules-file= +--sort-amount +--strict +--transpose +--tree +--unmarked +--value= +--version +--weekly +--yearly +__TEXT__ + +read -r -d "" _hledger_complist_options_balancesheet <<"__TEXT__" +--alias= +--anon +--auto +--average +--begin= +--change +--cleared +--color= +--cost +--cumulative +--daily +--date2 +--debug= +--depth= +--drop= +--empty +--end= +--exchange= +--file= +--flat +--forecast +--format= +--help +--historical +--ignore-assertions +--infer-market-price +--info +--man +--market +--monthly +--no-elide +--no-total +--output-file= +--output-format= +--pending +--percent +--period= +--pivot= +--pretty-tables +--quarterly +--real +--row-total +--rules-file= +--sort-amount +--strict +--tree +--unmarked +--value= +--version +--weekly +--yearly +__TEXT__ + +read -r -d "" _hledger_complist_options_balancesheetequity <<"__TEXT__" +--alias= +--anon +--auto +--average +--begin= +--change +--cleared +--color= +--cost +--cumulative +--daily +--date2 +--debug= +--depth= +--drop= +--empty +--end= +--exchange= +--file= +--flat +--forecast +--format= +--help +--historical +--ignore-assertions +--infer-market-price +--info +--man +--market +--monthly +--no-elide +--no-total +--output-file= +--output-format= +--pending +--percent +--period= +--pivot= +--pretty-tables +--quarterly +--real +--row-total +--rules-file= +--sort-amount +--strict +--tree +--unmarked +--value= +--version +--weekly +--yearly +__TEXT__ + +read -r -d "" _hledger_complist_options_bs <<"__TEXT__" +--alias= +--anon +--auto +--average +--begin= +--change +--cleared +--color= +--cost +--cumulative +--daily +--date2 +--debug= +--depth= +--drop= +--empty +--end= +--exchange= +--file= +--flat +--forecast +--format= +--help +--historical +--ignore-assertions +--infer-market-price +--info +--man +--market +--monthly +--no-elide +--no-total +--output-file= +--output-format= +--pending +--percent +--period= +--pivot= +--pretty-tables +--quarterly +--real +--row-total +--rules-file= +--sort-amount +--strict +--tree +--unmarked +--value= +--version +--weekly +--yearly +__TEXT__ + +read -r -d "" _hledger_complist_options_bse <<"__TEXT__" +--alias= +--anon +--auto +--average +--begin= +--change +--cleared +--color= +--cost +--cumulative +--daily +--date2 +--debug= +--depth= +--drop= +--empty +--end= +--exchange= +--file= +--flat +--forecast +--format= +--help +--historical +--ignore-assertions +--infer-market-price +--info +--man +--market +--monthly +--no-elide +--no-total +--output-file= +--output-format= +--pending +--percent +--period= +--pivot= +--pretty-tables +--quarterly +--real +--row-total +--rules-file= +--sort-amount +--strict +--tree +--unmarked +--value= +--version +--weekly +--yearly +__TEXT__ + +read -r -d "" _hledger_complist_options_cashflow <<"__TEXT__" +--alias= +--anon +--auto +--average +--begin= +--change +--cleared +--color= +--cost +--cumulative +--daily +--date2 +--debug= +--depth= +--drop= +--empty +--end= +--exchange= +--file= +--flat +--forecast +--format= +--help +--historical +--ignore-assertions +--infer-market-price +--info +--man +--market +--monthly +--no-elide +--no-total +--output-file= +--output-format= +--pending +--percent +--period= +--pivot= +--pretty-tables +--quarterly +--real +--row-total +--rules-file= +--sort-amount +--strict +--tree +--unmarked +--value= +--version +--weekly +--yearly +__TEXT__ + +read -r -d "" _hledger_complist_options_cf <<"__TEXT__" +--alias= +--anon +--auto +--average +--begin= +--change +--cleared +--color= +--cost +--cumulative +--daily +--date2 +--debug= +--depth= +--drop= +--empty +--end= +--exchange= +--file= +--flat +--forecast +--format= +--help +--historical +--ignore-assertions +--infer-market-price +--info +--man +--market +--monthly +--no-elide +--no-total +--output-file= +--output-format= +--pending +--percent +--period= +--pivot= +--pretty-tables +--quarterly +--real +--row-total +--rules-file= +--sort-amount +--strict +--tree +--unmarked +--value= +--version +--weekly +--yearly +__TEXT__ + +read -r -d "" _hledger_complist_options_check <<"__TEXT__" +--alias= +--anon +--auto +--begin= +--cleared +--color= --cost --daily --date2 ---debug ---depth +--debug= +--depth= --empty ---end ---exchange ---file +--end= +--exchange= +--file= --forecast --help --ignore-assertions --infer-market-price +--info +--man --market --monthly --pending ---period ---pivot +--period= +--pivot= --quarterly --real ---rules-file +--rules-file= +--strict --unmarked ---value +--value= --version --weekly --yearly --B --C --D --E --I --M --N --P --Q --R --U --V --W --X --Y --b --e --f --h --p -TEXT +__TEXT__ -cat < "$_HLEDGER_COMPLETION_TEMPDIR/options-commodities.txt" ---alias ---anon ---debug ---file ---help ---ignore-assertions ---pivot ---rules-file ---version --I --f --h -TEXT - -cat < "$_HLEDGER_COMPLETION_TEMPDIR/options-descriptions.txt" ---alias +read -r -d "" _hledger_complist_options_close <<"__TEXT__" +--alias= --anon --auto ---aux-date ---begin +--begin= --cleared ---colour +--close +--close-acct= +--close-desc= +--color= --cost --daily --date2 ---debug ---depth +--debug= +--depth= --empty ---end ---exchange ---file +--end= +--exchange= +--explicit +--file= --forecast --help --ignore-assertions --infer-market-price +--info +--interleaved +--man --market --monthly +--open +--open-acct= +--open-desc= --pending ---period ---pivot +--period= +--pivot= --quarterly --real ---rules-file +--rules-file= +--show-costs +--strict --unmarked ---value +--value= --version --weekly --yearly --B --C --D --E --I --M --N --P --Q --R --U --V --W --X --Y --b --e --f --h --p -TEXT +__TEXT__ -cat < "$_HLEDGER_COMPLETION_TEMPDIR/options-files.txt" ---alias ---anon ---debug ---file ---help ---ignore-assertions ---pivot ---rules-file ---version --I --f --h -TEXT - -cat < "$_HLEDGER_COMPLETION_TEMPDIR/options-notes.txt" ---alias +read -r -d "" _hledger_complist_options_codes <<"__TEXT__" +--alias= --anon --auto ---aux-date ---begin +--begin= --cleared ---colour +--color= --cost --daily --date2 ---debug ---depth +--debug= +--depth= --empty ---end ---exchange ---file +--end= +--exchange= +--file= --forecast --help --ignore-assertions --infer-market-price +--info +--man --market --monthly --pending ---period ---pivot +--period= +--pivot= --quarterly --real ---rules-file +--rules-file= +--strict --unmarked ---value +--value= --version --weekly --yearly --B --C --D --E --I --M --N --P --Q --R --U --V --W --X --Y --b --e --f --h --p -TEXT +__TEXT__ -cat < "$_HLEDGER_COMPLETION_TEMPDIR/options-payees.txt" ---alias +read -r -d "" _hledger_complist_options_commodities <<"__TEXT__" +--alias= +--anon +--debug= +--file= +--help +--ignore-assertions +--info +--man +--pivot= +--rules-file= +--strict +--version +__TEXT__ + +read -r -d "" _hledger_complist_options_descriptions <<"__TEXT__" +--alias= --anon --auto ---aux-date ---begin +--begin= --cleared ---colour +--color= --cost --daily --date2 ---debug ---depth +--debug= +--depth= --empty ---end ---exchange ---file +--end= +--exchange= +--file= --forecast --help --ignore-assertions --infer-market-price +--info +--man --market --monthly --pending ---period ---pivot +--period= +--pivot= --quarterly --real ---rules-file +--rules-file= +--strict --unmarked ---value +--value= --version --weekly --yearly --B --C --D --E --I --M --N --P --Q --R --U --V --W --X --Y --b --e --f --h --p -TEXT +__TEXT__ -cat < "$_HLEDGER_COMPLETION_TEMPDIR/options-prices.txt" ---alias +read -r -d "" _hledger_complist_options_diff <<"__TEXT__" +--alias= +--anon +--debug= +--file= +--help +--ignore-assertions +--info +--man +--pivot= +--rules-file= +--strict +--version +__TEXT__ + +read -r -d "" _hledger_complist_options_files <<"__TEXT__" +--alias= +--anon +--debug= +--file= +--help +--ignore-assertions +--info +--man +--pivot= +--rules-file= +--strict +--version +__TEXT__ + +read -r -d "" _hledger_complist_options_help <<"__TEXT__" +--help +__TEXT__ + +read -r -d "" _hledger_complist_options_import <<"__TEXT__" +--alias= --anon --auto ---aux-date ---begin +--begin= +--catchup --cleared ---colour +--color= +--cost +--daily +--date2 +--debug= +--depth= +--dry-run +--empty +--end= +--exchange= +--file= +--forecast +--help +--ignore-assertions +--infer-market-price +--info +--man +--market +--monthly +--pending +--period= +--pivot= +--quarterly +--real +--rules-file= +--strict +--unmarked +--value= +--version +--weekly +--yearly +__TEXT__ + +read -r -d "" _hledger_complist_options_incomestatement <<"__TEXT__" +--alias= +--anon +--auto +--average +--begin= +--change +--cleared +--color= +--cost +--cumulative +--daily +--date2 +--debug= +--depth= +--drop= +--empty +--end= +--exchange= +--file= +--flat +--forecast +--format= +--help +--historical +--ignore-assertions +--infer-market-price +--info +--man +--market +--monthly +--no-elide +--no-total +--output-file= +--output-format= +--pending +--percent +--period= +--pivot= +--pretty-tables +--quarterly +--real +--row-total +--rules-file= +--sort-amount +--strict +--tree +--unmarked +--value= +--version +--weekly +--yearly +__TEXT__ + +read -r -d "" _hledger_complist_options_is <<"__TEXT__" +--alias= +--anon +--auto +--average +--begin= +--change +--cleared +--color= +--cost +--cumulative +--daily +--date2 +--debug= +--depth= +--drop= +--empty +--end= +--exchange= +--file= +--flat +--forecast +--format= +--help +--historical +--ignore-assertions +--infer-market-price +--info +--man +--market +--monthly +--no-elide +--no-total +--output-file= +--output-format= +--pending +--percent +--period= +--pivot= +--pretty-tables +--quarterly +--real +--row-total +--rules-file= +--sort-amount +--strict +--tree +--unmarked +--value= +--version +--weekly +--yearly +__TEXT__ + +read -r -d "" _hledger_complist_options_notes <<"__TEXT__" +--alias= +--anon +--auto +--begin= +--cleared +--color= +--cost +--daily +--date2 +--debug= +--depth= +--empty +--end= +--exchange= +--file= +--forecast +--help +--ignore-assertions +--infer-market-price +--info +--man +--market +--monthly +--pending +--period= +--pivot= +--quarterly +--real +--rules-file= +--strict +--unmarked +--value= +--version +--weekly +--yearly +__TEXT__ + +read -r -d "" _hledger_complist_options_payees <<"__TEXT__" +--alias= +--anon +--auto +--begin= +--cleared +--color= +--cost +--daily +--date2 +--debug= +--declared +--depth= +--empty +--end= +--exchange= +--file= +--forecast +--help +--ignore-assertions +--infer-market-price +--info +--man +--market +--monthly +--pending +--period= +--pivot= +--quarterly +--real +--rules-file= +--strict +--unmarked +--used +--value= +--version +--weekly +--yearly +__TEXT__ + +read -r -d "" _hledger_complist_options_prices <<"__TEXT__" +--alias= +--anon +--auto +--begin= +--cleared +--color= --cost --costs --daily --date2 ---debug ---depth +--debug= +--depth= --empty ---end ---exchange ---file +--end= +--exchange= +--file= --forecast --help --ignore-assertions --infer-market-price +--info --inverted-costs +--man --market --monthly --pending ---period ---pivot +--period= +--pivot= --quarterly --real ---rules-file +--rules-file= +--strict --unmarked ---value +--value= --version --weekly --yearly --B --C --D --E --I --M --N --P --Q --R --U --V --W --X --Y --b --e --f --h --p -TEXT +__TEXT__ -cat < "$_HLEDGER_COMPLETION_TEMPDIR/options-print.txt" ---alias +read -r -d "" _hledger_complist_options_print <<"__TEXT__" +--alias= --anon --auto ---aux-date ---begin +--begin= --cleared ---colour +--color= --cost --daily --date2 ---debug ---depth +--debug= +--depth= --empty ---end ---exchange +--end= +--exchange= --explicit ---file +--file= --forecast --help --ignore-assertions --infer-market-price +--info +--man --market ---match +--match= --monthly --new ---output-file ---output-format +--output-file= +--output-format= --pending ---period ---pivot +--period= +--pivot= --quarterly --real ---rules-file +--rules-file= +--strict --unmarked ---value +--value= --version --weekly --yearly --B --C --D --E --I --M --N --O --P --Q --R --U --V --W --X --Y --b --e --f --h --m --o --p --x -TEXT +__TEXT__ -cat < "$_HLEDGER_COMPLETION_TEMPDIR/options-print-unique.txt" ---alias +read -r -d "" _hledger_complist_options_print_unique <<"__TEXT__" +--alias= --anon --auto ---aux-date ---begin +--begin= --cleared ---colour +--color= --cost --daily --date2 ---debug ---depth +--debug= +--depth= --empty ---end ---exchange ---file +--end= +--exchange= +--file= --forecast --help --ignore-assertions --infer-market-price +--info +--man --market --monthly --pending ---period ---pivot +--period= +--pivot= --quarterly --real ---rules-file +--rules-file= +--strict --unmarked ---value +--value= --version --weekly --yearly --B --C --D --E --I --M --N --P --Q --R --U --V --W --X --Y --b --e --f --h --p -TEXT +__TEXT__ -cat < "$_HLEDGER_COMPLETION_TEMPDIR/options-register.txt" ---alias +read -r -d "" _hledger_complist_options_reg <<"__TEXT__" +--alias= --anon --auto ---aux-date --average ---begin +--begin= --cleared ---colour +--color= --cost --cumulative --daily --date2 ---debug ---depth +--debug= +--depth= --empty ---end ---exchange ---file +--end= +--exchange= +--file= --forecast --help --historical --ignore-assertions --infer-market-price +--info --invert +--man --market --monthly ---output-file ---output-format +--output-file= +--output-format= --pending ---period ---pivot +--period= +--pivot= --quarterly --real --related ---rules-file +--rules-file= +--strict --unmarked ---value +--value= --version --weekly ---width +--width= --yearly --A --B --C --D --E --H --I --M --N --O --P --Q --R --U --V --W --X --Y --b --e --f --h --o --p --r --w -TEXT +__TEXT__ -cat < "$_HLEDGER_COMPLETION_TEMPDIR/options-register-match.txt" ---alias +read -r -d "" _hledger_complist_options_register <<"__TEXT__" +--alias= --anon --auto ---aux-date ---begin +--average +--begin= --cleared ---colour +--color= +--cost +--cumulative +--daily +--date2 +--debug= +--depth= +--empty +--end= +--exchange= +--file= +--forecast +--help +--historical +--ignore-assertions +--infer-market-price +--info +--invert +--man +--market +--monthly +--output-file= +--output-format= +--pending +--period= +--pivot= +--quarterly +--real +--related +--rules-file= +--strict +--unmarked +--value= +--version +--weekly +--width= +--yearly +__TEXT__ + +read -r -d "" _hledger_complist_options_register_match <<"__TEXT__" +--alias= +--anon +--auto +--begin= +--cleared +--color= --cost --daily --date2 ---debug ---depth +--debug= +--depth= --empty ---end ---exchange ---file +--end= +--exchange= +--file= --forecast --help --ignore-assertions --infer-market-price +--info +--man --market --monthly --pending ---period ---pivot +--period= +--pivot= --quarterly --real ---rules-file +--rules-file= +--strict --unmarked ---value +--value= --version --weekly --yearly --B --C --D --E --I --M --N --P --Q --R --U --V --W --X --Y --b --e --f --h --p -TEXT +__TEXT__ -cat < "$_HLEDGER_COMPLETION_TEMPDIR/options-stats.txt" ---alias +read -r -d "" _hledger_complist_options_rewrite <<"__TEXT__" +--add-posting= +--alias= --anon --auto ---aux-date ---begin +--begin= --cleared ---colour +--color= --cost --daily --date2 ---debug ---depth +--debug= +--depth= +--diff --empty ---end ---exchange ---file +--end= +--exchange= +--file= --forecast --help --ignore-assertions --infer-market-price +--info +--man --market --monthly ---output-file --pending ---period ---pivot +--period= +--pivot= --quarterly --real ---rules-file +--rules-file= +--strict --unmarked ---value +--value= --version --weekly --yearly --B --C --D --E --I --M --N --P --Q --R --U --V --W --X --Y --b --e --f --h --o --p -TEXT +__TEXT__ -cat < "$_HLEDGER_COMPLETION_TEMPDIR/options-tags.txt" ---alias +read -r -d "" _hledger_complist_options_roi <<"__TEXT__" +--alias= --anon --auto ---aux-date ---begin +--begin= +--cashflow --cleared ---colour +--color= --cost --daily --date2 ---debug ---depth +--debug= +--depth= --empty ---end ---exchange ---file +--end= +--exchange= +--file= --forecast --help --ignore-assertions --infer-market-price +--info +--investment= +--man +--market +--monthly +--pending +--period= +--pivot= +--profit-loss= +--quarterly +--real +--rules-file= +--strict +--unmarked +--value= +--version +--weekly +--yearly +__TEXT__ + +read -r -d "" _hledger_complist_options_stats <<"__TEXT__" +--alias= +--anon +--auto +--begin= +--cleared +--color= +--cost +--daily +--date2 +--debug= +--depth= +--empty +--end= +--exchange= +--file= +--forecast +--help +--ignore-assertions +--infer-market-price +--info +--man +--market +--monthly +--output-file= +--pending +--period= +--pivot= +--quarterly +--real +--rules-file= +--strict +--unmarked +--value= +--version +--weekly +--yearly +__TEXT__ + +read -r -d "" _hledger_complist_options_tags <<"__TEXT__" +--alias= +--anon +--auto +--begin= +--cleared +--color= +--cost +--daily +--date2 +--debug= +--depth= +--empty +--end= +--exchange= +--file= +--forecast +--help +--ignore-assertions +--infer-market-price +--info +--man --market --monthly --parsed --pending ---period ---pivot +--period= +--pivot= --quarterly --real ---rules-file +--rules-file= +--strict --unmarked ---value +--value= --values --version --weekly --yearly --B --C --D --E --I --M --N --P --Q --R --U --V --W --X --Y --b --e --f --h --p -TEXT +__TEXT__ -cat < "$_HLEDGER_COMPLETION_TEMPDIR/options-test.txt" ---color ---debug ---help ---version --h --p -TEXT - -cat < "$_HLEDGER_COMPLETION_TEMPDIR/options-help.txt" ---cat +read -r -d "" _hledger_complist_options_test <<"__TEXT__" +--debug= --help --info --man ---pager --h -TEXT - -cat < "$_HLEDGER_COMPLETION_TEMPDIR/options-equity.txt" ---alias ---anon ---auto ---aux-date ---begin ---cleared ---close ---close-acct ---close-desc ---colour ---cost ---daily ---date2 ---debug ---depth ---empty ---end ---exchange ---explicit ---file ---forecast ---help ---ignore-assertions ---infer-market-price ---interleaved ---market ---monthly ---open ---open-acct ---open-desc ---pending ---period ---pivot ---quarterly ---real ---rules-file ---show-costs ---unmarked ---value --version ---weekly ---x ---yearly --5 --B --C --D --E --I --M --N --P --Q --R --U --V --W --X --Y --b --e --f --h --p --x -TEXT +__TEXT__ -cat < "$_HLEDGER_COMPLETION_TEMPDIR/options-areg.txt" ---alias +read -r -d "" _hledger_complist_options_ui <<"__TEXT__" +--alias= --anon --auto ---aux-date ---begin ---cleared ---colour ---cost ---daily ---date2 ---debug ---depth ---empty ---end ---exchange ---file ---forecast ---help ---ignore-assertions ---infer-market-price ---market ---monthly ---no-elide ---output-file ---output-format ---pending ---period ---pivot ---quarterly ---real ---rules-file ---txn-dates ---unmarked ---value ---version ---weekly ---width ---yearly --B --C --D --E --H --I --M --N --O --P --Q --R --U --V --W --X --Y --b --e --f --h --o --p --w -TEXT - -cat < "$_HLEDGER_COMPLETION_TEMPDIR/options-bs.txt" ---alias ---anon ---auto ---aux-date ---average ---begin +--begin= --change --cleared ---colour ---cost ---cumulative ---daily ---date2 ---debug ---depth ---drop ---empty ---end ---exchange ---file ---flat ---forecast ---format ---help ---historical ---ignore-assertions ---infer-market-price ---market ---monthly ---no-elide ---no-total ---output-file ---output-format ---pending ---percent ---period ---pivot ---pretty-tables ---quarterly ---real ---row-total ---rules-file ---sort-amount ---tree ---unmarked ---value ---version ---weekly ---yearly --A --B --C --D --E --H --I --M --N --O --P --Q --R --S --T --U --V --W --X --Y --b --e --f --h --l --o --p --t -TEXT - -cat < "$_HLEDGER_COMPLETION_TEMPDIR/options-bse.txt" ---alias ---anon ---auto ---aux-date ---average ---begin ---change ---cleared ---colour ---cost ---cumulative ---daily ---date2 ---debug ---depth ---drop ---empty ---end ---exchange ---file ---flat ---forecast ---format ---help ---historical ---ignore-assertions ---infer-market-price ---market ---monthly ---no-elide ---no-total ---output-file ---output-format ---pending ---percent ---period ---pivot ---pretty-tables ---quarterly ---real ---row-total ---rules-file ---sort-amount ---tree ---unmarked ---value ---version ---weekly ---yearly --A --B --C --D --E --H --I --M --N --O --P --Q --R --S --T --U --V --W --X --Y --b --e --f --h --l --o --p --t -TEXT - -cat < "$_HLEDGER_COMPLETION_TEMPDIR/options-cf.txt" ---alias ---anon ---auto ---aux-date ---average ---begin ---change ---cleared ---colour ---cost ---cumulative ---daily ---date2 ---debug ---depth ---drop ---empty ---end ---exchange ---file ---flat ---forecast ---format ---help ---historical ---ignore-assertions ---infer-market-price ---market ---monthly ---no-elide ---no-total ---output-file ---output-format ---pending ---percent ---period ---pivot ---pretty-tables ---quarterly ---real ---row-total ---rules-file ---sort-amount ---tree ---unmarked ---value ---version ---weekly ---yearly --A --B --C --D --E --H --I --M --N --O --P --Q --R --S --T --U --V --W --X --Y --b --e --f --h --l --o --p --t -TEXT - -cat < "$_HLEDGER_COMPLETION_TEMPDIR/options-is.txt" ---alias ---anon ---auto ---aux-date ---average ---begin ---change ---cleared ---colour ---cost ---cumulative ---daily ---date2 ---debug ---depth ---drop ---empty ---end ---exchange ---file ---flat ---forecast ---format ---help ---historical ---ignore-assertions ---infer-market-price ---market ---monthly ---no-elide ---no-total ---output-file ---output-format ---pending ---percent ---period ---pivot ---pretty-tables ---quarterly ---real ---row-total ---rules-file ---sort-amount ---tree ---unmarked ---value ---version ---weekly ---yearly --A --B --C --D --E --H --I --M --N --O --P --Q --R --S --T --U --V --W --X --Y --b --e --f --h --l --o --p --t -TEXT - -cat < "$_HLEDGER_COMPLETION_TEMPDIR/options-ui.txt" ---alias ---anon ---auto ---aux-date ---begin ---change ---cleared ---colour +--color= --cost --daily --date2 ---debug ---depth +--debug= +--depth= --empty ---end ---exchange ---file +--end= +--exchange= +--file= --flat --forecast --help --ignore-assertions --infer-market-price +--info +--man --market --monthly --pending ---period ---pivot +--period= +--pivot= --quarterly --real ---register ---rules-file ---theme +--register= +--rules-file= +--strict +--theme= --tree --unmarked ---value +--value= --version --watch --weekly --yearly --B --C --D --E --I --M --N --P --Q --R --U --V --W --X --Y --b --e --f --h --l --p --t -TEXT +__TEXT__ -cat < "$_HLEDGER_COMPLETION_TEMPDIR/options-web.txt" ---alias +read -r -d "" _hledger_complist_options_web <<"__TEXT__" +--alias= --anon --auto ---aux-date ---base-url ---begin ---capabilities ---capabilities-header +--base-url= +--begin= +--capabilities= +--capabilities-header= --cleared ---colour ---cors +--color= +--cors= --cost --daily --date2 ---debug ---depth +--debug= +--depth= --empty ---end ---exchange ---file ---file-url +--end= +--exchange= +--file= +--file-url= --forecast --help ---host +--host= --ignore-assertions --infer-market-price +--info +--man --market --monthly --pending ---period ---pivot ---port +--period= +--pivot= +--port= --quarterly --real ---rules-file +--rules-file= --serve ---server ---socket +--serve-api +--socket= +--strict +--test --unmarked ---value +--value= --version --weekly --yearly --B --C --D --E --I --M --N --P --Q --R --U --V --W --X --Y --b --e --f --h --p -TEXT - -cat < "$_HLEDGER_COMPLETION_TEMPDIR/options-api.txt" ---file ---help ---host ---port ---static-dir ---swagger ---version --d --f --h --p -TEXT +__TEXT__ +return 0 diff --git a/shell-completion/hledger-completion.bash.m4 b/shell-completion/hledger-completion.bash.m4 index 5542affdc..ff87f59ae 100644 --- a/shell-completion/hledger-completion.bash.m4 +++ b/shell-completion/hledger-completion.bash.m4 @@ -1,132 +1,29 @@ - -# 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 - -# Note: grep "^$wordToComplete" is (functional) not safe to use if the word -# contains regex special chars. But it might be no problem because of -# COMP_WORDBREAKS. - -# Note: compgen and compopt is pretty complicated. Piping to -# grep "^$wordToComplete" -# seems like a hack - I'd rather use -# compgen ... -- "$wordToComplete" -# But what options to use? I don't want to use -W because it may exceed the -# maximum command line length. -C "cat file" is not working either. It would be -# best if compgen could read from stdin but it does not. - -# Note: Working with bash arrays is nasty compared to editing a text file. -# Consider for example grepping an array or mapping a substitution on it. -# Therefore, we create temp files in RAM for completion suggestions (see below). - -readonly _HLEDGER_COMPLETION_TEMPDIR=$(mktemp -d) - -_hledger_completion_function() { - #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 - COMPREPLY+=( $(grep -h "^$wordToComplete" -- "$_HLEDGER_COMPLETION_TEMPDIR/options-$subcommand.txt") ) - break - fi - subcommand= - done - - if [[ -z $subcommand ]]; then - - declare completeFiles filenameSoFar - case $precedingWord in - -f|--file|--rules-file) - completeFiles=1 - filenameSoFar=$wordToComplete - ;; - =) - completeFiles=1 - filenameSoFar=$wordToComplete - ;; - esac - - if [[ -n $completeFiles ]]; then - #COMP_WORDBREAKS='= ' - declare -a files - # This does not work because assignment to 'files' in the "pipe - # subshell" has no effect! - #compgen -df | grep "^$filenameSoFar" | readarray -t files - - compopt -o filenames -o dirnames - readarray -t files < <(compgen -f -- "$filenameSoFar") - COMPREPLY=( "${files[@]}" ) - - else - COMPREPLY+=( $(grep -h "^$wordToComplete" -- "$_HLEDGER_COMPLETION_TEMPDIR/commands.txt" "$_HLEDGER_COMPLETION_TEMPDIR/generic-options.txt") ) - fi - - else - - # Almost all subcommands accept [QUERY] - # -> always add accounts to completion list - - # TODO Get ledger file from -f --file arguments from COMP_WORDS and pass it to - # the 'hledger accounts' call. Note that --rules-file - if present - must also - # be passed! - - declare -a accounts - readarray -t accounts < <({ cat "$_HLEDGER_COMPLETION_TEMPDIR/query-filters.txt"; hledger accounts --flat; } | grep "^$wordToComplete") - compopt -o nospace - COMPREPLY+=( "${accounts[@]}" ) - # Special characters (e.g. '-', ':') are allowed in account names. - # Account names with spaces must be still be quoted (e.g. '"Expens') - # for completion. Setting COMP_WORDBREAKS='' would not help here! - COMP_WORDBREAKS=' ' - - fi - -} - -_hledger_extension_completion_function() { - declare cmd=$1 - - # Change parameters and arguments and call the - # normal hledger completion function. - declare extensionName=${cmd#*-} - export -a COMP_WORDS=( "hledger" "$extensionName" "${COMP_WORDS[@]:1}" ) - #echo; echo "debug: ${COMP_WORDS[@]}" - shift - _hledger_completion_function "hledger" "$@" -} - -# Register completion function for hledger: -complete -F _hledger_completion_function hledger - -# Register completion functions for hledger extensions: -complete -F _hledger_extension_completion_function hledger-ui -complete -F _hledger_extension_completion_function hledger-web +undivert(`hledger-completion.bash.stub')dnl # Include lists of commands and options generated by the Makefile using the # m4 macro processor. # Included files must have exactly one newline at EOF to prevent weired errors. -cat < "$_HLEDGER_COMPLETION_TEMPDIR/commands.txt" -include(`commands.txt')dnl -TEXT +read -r -d "" _hledger_complist_commands <<"__TEXT__" +undivert(`commands.txt')dnl +__TEXT__ -cat < "$_HLEDGER_COMPLETION_TEMPDIR/query-filters.txt" -include(`query-filters.txt')dnl -TEXT +read -r -d "" _hledger_complist_query_filters <<"__TEXT__" +undivert(`query-filters.txt')dnl +__TEXT__ -cat < "$_HLEDGER_COMPLETION_TEMPDIR/generic-options.txt" -include(`generic-options.txt')dnl -TEXT - -include(`foreach2.m4') +read -r -d "" _hledger_complist_generic_options <<"__TEXT__" +undivert(`generic-options.txt')dnl +__TEXT__ +# Dashes are replaced by m4 with underscores to form valid identifiers +# Referenced by indirect expansion of $subcommandOptions +dnl +include(`foreach2.m4')dnl foreach(`cmd', (include(`commands-list.txt')), ` -cat < "$_HLEDGER_COMPLETION_TEMPDIR/options-cmd.txt" -include(options-cmd.txt)dnl -TEXT -') +read -r -d "" _hledger_complist_options_`'translit(cmd, -, _) <<"__TEXT__" +undivert(options-cmd.txt)dnl +__TEXT__ +')dnl + +return 0 diff --git a/shell-completion/hledger-completion.bash.stub b/shell-completion/hledger-completion.bash.stub new file mode 100644 index 000000000..2c4862615 --- /dev/null +++ b/shell-completion/hledger-completion.bash.stub @@ -0,0 +1,403 @@ +# -*- mode: sh; sh-basic-offset: 4; indent-tabs-mode: nil -*- +# ex: ft=sh ts=4 sw=4 et +# shellcheck disable=2034,2154 + +# Completion script for hledger. +# Created using a Makefile and real hledger. + +# This script is sourced by an interactive shell, so do NOT do things like +# 'set -o pipefail' or mangle the global environment in any other way! +# That said, we *do* remove colon (:) from COMP_WORDBREAKS which impacts +# the rest of the session and completion for other programs. + +# INSTALLATION: +# To install you can simply source this file from your shell's startup files. +# +# Alternatively, copy/symlink it into `${BASH_COMPLETION_USER_DIR}/completions` +# or `${XDG_DATA_HOME:-$HOME/.local/share}/bash-completion/completions`, rename +# it to either `hledger`, `_hledger` or `hledger.bash`, and it will be loaded +# dynamically the first time you use the `hledger` command. Optionally, create +# symlinks to this file for any extensions used e.g.: +# +# mkdir -p "${BASH_COMPLETION_USER_DIR:-${XDG_DATA_HOME:-$HOME/.local/share}/bash-completion}/completions" && +# cd "${BASH_COMPLETION_USER_DIR:-${XDG_DATA_HOME:-$HOME/.local/share}/bash-completion}/completions" && +# cp /path/to/hledger-completion.bash hledger && +# ln -s hledger hledger-ui && +# ln -s hledger hledger-web && +# : done. + + +_hledger_completion() { + local cur prev words cword + _init_completion -n : || return 0 + + # Current treatment for special characters: + # - exclude colon (:) from COMP_WORDBREAKS + # - option processing assumes that `=` is in COMP_WORDBREAKS + # - use compopt -o filenames selectively to escape the rest + COMP_WORDBREAKS=${COMP_WORDBREAKS//:} + case $COMP_WORDBREAKS in + *=*) : ;; + *) COMP_WORDBREAKS=$COMP_WORDBREAKS= ;; + esac + + local subcommand + local subcommandOptions + local i + for ((i=1; i<${#words[@]}; i++)); do + subcommand=${words[i]} + if ! grep -Fxqe "$subcommand" <<< "$_hledger_complist_commands"; then + subcommand= + continue + fi + # There could be other commands begining with $subcommand, e.g.: + # $subcommand == reg --> register, register-match, + # $subcommand == bal --> balance, balancesheet, balancesheetequity, etc. + # Do not ignore them! + if ((i == cword)); then + _hledger_compreply "$( + _hledger_compgen "$_hledger_complist_commands" + )" + return 0 + fi + + # Replace dashes with underscores and use indirect expansion + subcommandOptions=_hledger_complist_options_${subcommand//-/_} + + if [[ $cur == -* ]]; then + _hledger_compreply "$(_hledger_compgen "${!subcommandOptions}")" + # Suspend space on completion of long options requiring an argument + [[ ${COMPREPLY[0]} == --*= ]] && compopt -o nospace + + return 0 + fi + break + done + + # Option argument completion + _hledger_compreply_optarg && return + + if [[ -z $subcommand ]]; then + if [[ $cur == -* ]]; then + _hledger_compreply "$( + _hledger_compgen "$_hledger_complist_generic_options" + )" + # Suspend space on completion of long options requiring an argument + [[ ${COMPREPLY[0]} == --*= ]] && compopt -o nospace + else + _hledger_compreply "$( + _hledger_compgen "$_hledger_complist_commands" + )" + fi + + return 0 + fi + + # Set this from here on because queries tend to have lots of special chars + # TODO: better handling of special characters + compopt -o filenames + + # Query completion + _hledger_compreply_query && return + + # Subcommand specific + case $subcommand in + # These do not expect or support any query arguments + commodities|check|files|help|import|print-unique|test) + return 0 + ;; + esac + + # Offer query filters and accounts for the rest + _hledger_compreply "$(_hledger_compgen "$_hledger_complist_query_filters")" + if [[ -z $cur ]]; then + _hledger_compreply_append "$( + _hledger_compgen "$(_hledger accounts --flat --depth 1)" + )" + else + _hledger_compreply_append "$( + _hledger_compgen "$(_hledger accounts --flat)" + )" + fi + + # Suspend space on completion of query prefix + # Do not sort, keep accounts and query filters grouped separately + [[ ${COMPREPLY[0]} == *: ]] && compopt -o nospace + compopt -o nosort + + return 0 +} + +_hledger_extension_completion() { + local cmd=${1##*/} + local ext=${cmd#hledger-} + # Pretend that hledger is called with the given extension + # as the first argument and call main completion function + COMP_WORDS=("hledger" "$ext" "${COMP_WORDS[@]:1}") + COMP_CWORD=$((COMP_CWORD + 1)) + _hledger_completion "hledger" "${@:1}" +} + +# Register completion function for hledger: +complete -F _hledger_completion hledger + +# Register completion functions for hledger extensions: +complete -F _hledger_extension_completion hledger-ui hledger-web + +# Helpers + +# Comment out when done +_hledger_debug() { + ((HLEDGER_DEBUG)) || return 0 + local var vars=(words) + (($#)) && vars=("$@") + for var in "${vars[@]}"; do + printf '\ndebug: %s\n' "$(declare -p "$var")" >&2 + done +} + +# Stolen from bash-completion +# This function quotes the argument in a way so that readline dequoting +# results in the original argument. This is necessary for at least +# `compgen' which requires its arguments quoted/escaped: +_hledger_quote_by_ref() +{ + printf -v "$2" %q "$1" + + # If result becomes quoted like this: $'string', re-evaluate in order to + # drop the additional quoting. See also: http://www.mail-archive.com/ + # bash-completion-devel@lists.alioth.debian.org/msg01942.html + [[ ${!2} == \$* ]] && eval "$2=${!2}" +} + +# Set the value of COMPREPLY from newline delimited completion candidates +_hledger_compreply() { + local IFS=$'\n' + # shellcheck disable=2206 + COMPREPLY=($1) +} + +# Append the value of COMPREPLY from newline delimited completion candidates +_hledger_compreply_append() { + local IFS=$'\n' + # shellcheck disable=2206 + COMPREPLY+=($1) +} + +# Generate input suitable for _hledger_compreply() from newline delimited +# completion candidates. It doesn't seem there is a way to feed a literal +# word list to compgen -- it will eat your quotes, drink your booze and... +# Completion candidates are quoted accordingly first and then we leave it to +# compgen to deal with readline. +# +# Arguments: +# $1: a newline separated list with completion cadidates +# $2: (optional) a prefix string to add to generated completions +# $3: (optional) a word to match instead of $cur, the default. +# If $match is null and $prefix is defined the match is done against $cur +# stripped of $prefix. If both $prefix and $match are null we match against +# $cur and no prefix is added to completions. +_hledger_compgen() { + local complist=$1 + local prefix=$2 + local match=$3 + local quoted=() + local word + local i=0 + + while IFS= read -r word; do + _hledger_quote_by_ref "$word" word + quoted[i++]=$word + done <<< "$complist" + + if (($# < 3)); then + match=${cur:${#prefix}} + fi + + local IFS=$'\n' + compgen -P "$prefix" -W "${quoted[*]}" -- "$match" +} + +# Try required option argument completion. Set COMPREPLY and return 0 on +# success, 1 if option doesn't require an argument or out of context +_hledger_compreply_optarg() { + local option=${words[cword - 1]} + local match=$cur + local wordlist + + # Match the empty string on --file=, not the equal sign itself + if [[ $cur == = ]]; then + match="" + # Once input is present, cword is incremented so we compensate + elif [[ $prev == = ]]; then + option=${words[cword - 2]} + fi + + [[ $option == -* ]] || return + + case $option in + --alias) + compopt -o nospace -o filenames + _hledger_compreply "$( + _hledger_compgen "$(_hledger accounts --flat)" "" "$match" + )" + ;; + -f|--file|--rules-file|-o|--output-file) + compopt -o filenames + _hledger_compreply "$(compgen -f -- "$match")" + ;; + --pivot) + compopt -o nosort + wordlist="code description note payee" + _hledger_compreply "$(compgen -W "$wordlist" -- "$match")" + _hledger_compreply_append "$( + _hledger_compgen "$(_hledger tags)" "" "$match" + )" + ;; + --value) + wordlist="cost then end now" + _hledger_compreply "$(compgen -W "$wordlist" -- "$match")" + ;; + -X|--exchange) + _hledger_compreply "$( + _hledger_compgen "$(_hledger commodities)" "" "$match" + )" + ;; + --color|--colour) + compopt -o nosort + wordlist="auto always yes never no" + _hledger_compreply "$(compgen -W "$wordlist" -- "$match")" + ;; + -O|--output-format) + wordlist="txt csv json sql" + _hledger_compreply "$(compgen -W "$wordlist" -- "$match")" + ;; + --close-acct|--open-acct) + compopt -o filenames + _hledger_compreply "$( + _hledger_compgen "$(_hledger accounts --flat)" "" "$match" + )" + ;; + --debug) + wordlist="{1..9}" + _hledger_compreply "$(compgen -W "$wordlist" -- "$match")" + ;; + # Argument required, but no handler (yet) + -b|-e|-p) + _hledger_compreply "" + ;; + # Check if an unhandled long option requires an argument + *) + local optionList argRequired + + if [[ -n $subcommandOptions ]]; then + optionList=${!subcommandOptions} + else + optionList=$_hledger_complist_generic_options + fi + + while IFS= read -r argRequired; do + if [[ $argRequired == "$option=" ]]; then + _hledger_compreply "" + return 0 + fi + done <<< "$optionList" + + return 1 + ;; + esac + + return 0 +} + +# Query filter completion through introspection +_hledger_compreply_query() { + [[ $cur =~ .: ]] || return + local query=${cur%%:*}: + local match=${cur#*:} + grep -Fxqe "$query" <<< "$_hledger_complist_query_filters" || return + + local hledgerArgs=() + case $query in + acct:) + if (( ${#match} )); then + hledgerArgs=(accounts --flat) + else + hledgerArgs=(accounts --flat --depth 1) + fi + ;; + code:) hledgerArgs=(codes) ;; + cur:) hledgerArgs=(commodities) ;; + desc:) hledgerArgs=(descriptions) ;; + note:) hledgerArgs=(notes) ;; + payee:) hledgerArgs=(payees) ;; + tag:) hledgerArgs=(tags) ;; + *) + local wordlist + case $query in + amt:) wordlist="< <= > >=" ;; + real:) wordlist="\ 0" ;; + status:) wordlist="\ * !" ;; + *) return 1 ;; + esac + _hledger_compreply "$( + compgen -P "$query" -W "$wordlist" -- "$match" + )" + return 0 + ;; + esac + + _hledger_compreply "$( + _hledger_compgen "$(_hledger "${hledgerArgs[@]}")" "$query" + )" + + return 0 +} + +# Parse the command line so far and fill the array $optarg with the arguments to +# given options. $optarg should be declared by the caller +_hledger_optarg() { + local options=("$@") + local i j offset + optarg=() + + # hledger balance --file ~/ledger _ + # 0 1 2 3 4 + for ((i=1; i < ${#words[@]} - 2; i++)); do + offset=0 + for j in "${!options[@]}"; do + if [[ ${words[i]} == "${options[j]}" ]]; then + if [[ ${words[i+1]} == '=' ]]; then + offset=2 + else + offset=1 + fi + # Pass it through compgen to unescape it + optarg+=("$(compgen -W "${words[i + offset]}")") + fi + done + ((i += offset)) + done +} + +# Get ledger file from -f --file arguments from COMP_WORDS and pass it to the +# 'hledger' call. Note that --rules-file - if present - must also be passed! +# Multiple files are allowed so pass them all in the order of appearance. +_hledger() { + local hledgerArgs=("$@") + local file + local -a optarg + + _hledger_optarg -f --file + for file in "${optarg[@]}"; do + [[ -f $file ]] && hledgerArgs+=(--file "$file") + done + + _hledger_optarg --rules-file + for file in "${optarg[@]}"; do + [[ -f $file ]] && hledgerArgs+=(--rules-file "$file") + done + + # Discard errors. Is there a way to validate files before using them? + hledger "${hledgerArgs[@]}" 2>/dev/null +} diff --git a/shell-completion/output-commands.sh b/shell-completion/output-commands.sh deleted file mode 100755 index daa160f40..000000000 --- a/shell-completion/output-commands.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash -# Output subcommands from man/usage text - -set -o errexit -o pipefail -o nounset - -main() { - declare tmp - tmp=$(mktemp) - cat > "$tmp" - - # Do not output mistaken commands that start with a dash (e.g. -h) - sed -rn 's/^ ([-a-z]+).*/\1/gp' "$tmp" \ - | grep -v ^- - - # Output single command aliases in parenthesis: - # Do not output single letter command aliases, it's not useful. - sed -rn 's/^ .*\(([a-z]+)\).*/\1/gp' "$tmp" \ - | grep -v ^.$ - - # TODO missing: (reg, r) (multiple aliases) -} - -main "$@" diff --git a/shell-completion/output-options.sh b/shell-completion/output-options.sh deleted file mode 100755 index 3123e416b..000000000 --- a/shell-completion/output-options.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/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-Z][-_a-zA-Z0-9]*)=?.*/\1/gp' < "$tmp" -} - -main "$@" diff --git a/shell-completion/parse-commands.sh b/shell-completion/parse-commands.sh new file mode 100755 index 000000000..644a9936f --- /dev/null +++ b/shell-completion/parse-commands.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +# Parse hledger's help and output all commands and command aliases in +# parenthesis. Do not output single letter command aliases, it's not useful. +set -euo pipefail + +declare commands_help +commands_help=$(hledger) +{ + sed -rn 's/^[[:space:]]+([a-z][-a-z]+)[[:space:]]+.*/\1/p' <<< "$commands_help" + sed -rn 's/^[[:space:]]+[a-z][-a-z]+[[:space:]]+\(([a-z][ ,a-z]+)\).*/\1/p' <<< "$commands_help" | + sed 's/[[:space:]]*,[[:space:]]*/\n/g' | + sed '/^.$/d' +} | sed '/^hledger/d' | sort -u + +# Local Variables: +# mode: sh +# sh-basic-offset: 4 +# indent-tabs-mode: nil +# End: +# ex: ts=4 sw=4 et diff --git a/shell-completion/parse-options.sh b/shell-completion/parse-options.sh new file mode 100755 index 000000000..bc1495479 --- /dev/null +++ b/shell-completion/parse-options.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +# Parse hledger's help and output long options. Do not propose single letter +# completions. Options requiring an argument make that explicit by appending the +# equal sign (=) +set -euo pipefail + +declare subcommand=${1:-} +declare hledgerArgs=(--help) +[[ -n $subcommand ]] && hledgerArgs=("$subcommand" "${hledgerArgs[@]}") + +hledger "${hledgerArgs[@]}" | + sed -rn '/^[[:space:]]+-/p' | + sed -rn 's/^[[:space:]]{1,4}(-.)?[[:space:]]{1,4}(--[a-zA-Z][-_a-zA-Z0-9]+=?).*/\2/p' | + sort -u + +# Local Variables: +# mode: sh +# sh-basic-offset: 4 +# indent-tabs-mode: nil +# End: +# ex: ts=4 sw=4 et diff --git a/shell-completion/query-filters.txt b/shell-completion/query-filters.txt index cfb33747f..141d2fa30 100644 --- a/shell-completion/query-filters.txt +++ b/shell-completion/query-filters.txt @@ -1,22 +1,15 @@ -not: acct: amt: -amt:< -amt:<= -amt:> -amt:>= code: cur: -desc: date: date2: depth: +desc: +inacct: +not: note: payee: real: -real:0 status: -status:! -status:* tag: -inacct: