From 72b737a42f1d719d7888aac4ddf62fe4e0c5a2a5 Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Sun, 6 Dec 2020 13:00:17 +0100 Subject: [PATCH 01/82] Fix #1404, and more... This was supposed to be just a fix for #1404 but upon visiting the source several issues became apparent and that is why the commit grew a bit more than expected. A complete list of changes bellow: * Fix #1404 No more orphaned temporary directories. Commands, options, etc. that used to be stored in there are included at build-time as here documents in the source. * Fix artifacts in =/tmp= after build Upon fixing the above I became aware that the build itself was leaving behind a heap of artifacts in =/tmp= that were not taken care of with a ~make clean~. Fixed by using temporary files and directories in the build directory. Makefile and build scripts adjusted. * Produce command aliases Regular expressions in build scripts changed to produce all command aliases except single letter ones (see below) * Do not propose single letters completions It is simply not useful and adds a lot of noise. It makes completion slower as well because you need to hit yes on the prompt: > Display all 200 possibilities? (y or n) =output-options.sh= now excludes those. * Query filters simplified Keep only the prefix of the filter with the colon in =query-filters.txt=. This change has two reasons: - Single letter completions are not useful (see above) - It allows for completion suggestions specific to each - Bonus reason: it's a completion engine, not a user manual. * Fix completion impacts on global environment The completion script was making a couple of changes to the global environment which had an impact for the rest of the shell session. ~set -o pipefail~: the change is hidden from the user and could lead to subtle errors throughout the shell session COMP_WORDBREAKS=" ": this affects subsequent completions for us and other programs too. I exclude the colon =:= from its value and use ~compopt -o filenames~ to handle escaping of special characters for us. I would like to find a solution without messing with COMP_WORDBREAKS but it is not straight forward. * Fix hiding of legit subcommands Completion was hiding all possibilities if a subcommand happens to be the prefix of another. On typing ~balance~, one should be proposed ~balancesheet~ and ~balancesheetequity~ as well. * Return early Try to complete depending on the current context and return immediately if successful. Keep completion list relevant and as short as possible. * Context aware completion - Add handlers for option parameter completion, see _hledger_compreply_optarg() - Add handlers for query filters:, see _hledger_compreply_query() - Use --file and --rules-file arguments when proposing completions for the above, see _hledger() - Propose only top level accounts at first. Again, keep it short and focused. * Custom ~compgen~ wrapper ~compgen~ is fairly complicated. There is no way to feed it a word list with literals. It will mangle your input in so many ways that we cannot trust it. To work around this several wrappers are used: _hledger_compgen() works with _hledger_quote_by_ref() to process and escape newline separated input which is then fed to ~compgen~ and finally in ~COMPREPLY~ through _hledger_compreply() and _hledger_compreply_append(). It sounds messy and I guess it is, I would like to find a more straight forward way to do it. I think it is still a way better and safer interface with ~readline~ than trying to ~grep~ our way through. * Replace ~declare~ with ~local~ Again, this script is sourced by the shell -- keep variable scopes as narrow as possible. * Use ~compopt -o nosort~ Often I resort to using it to keep different groups of completions together. Whether this is more ergonomic or not is subjective. But our input lists are already sorted at build-time so why not. Sort manually =query-filters.txt= when changing it. * Remove irrelevant comments And add some new ones :) I think that is all. Give it a spin, try to abuse it, in and outside of quotes, with some funky accounts, payees, tags, whatever, and tell me where it breaks or behaves unexpectedly. --- shell-completion/Makefile | 5 +- shell-completion/hledger-completion.bash | 2972 ++++++++----------- shell-completion/hledger-completion.bash.m4 | 368 ++- shell-completion/output-commands.sh | 16 +- shell-completion/output-options.sh | 12 +- shell-completion/query-filters.txt | 13 +- 6 files changed, 1526 insertions(+), 1860 deletions(-) diff --git a/shell-completion/Makefile b/shell-completion/Makefile index fd1866a28..a838b5dc4 100644 --- a/shell-completion/Makefile +++ b/shell-completion/Makefile @@ -10,7 +10,7 @@ generic-options.txt: hledger -h | ./output-options.sh | sort -u > $@ commands.txt: - hledger | ./output-commands.sh | grep -v ^hledger > $@ + hledger | ./output-commands.sh | grep -v ^hledger | sort -u > $@ echo ui >> $@ echo web >> $@ echo api >> $@ @@ -23,8 +23,9 @@ commands-list.txt: commands.txt # hledger help --cat hledger | sed -n '/^QUERIES/,/^[A-Z]/p' command-options: commands.txt - parallel -j8 'hledger {} -h | ./output-options.sh | sort -u > options-{}.txt' < commands.txt + parallel -j8 'hledger {} -h | ./output-options.sh {} | sort -u > options-{}.txt' < commands.txt clean: rm -f commands*.txt generic-options.txt options-*.txt rm -f hledger-completion.bash + rm -rf _{commands,options}.tmp diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index c4b1dda77..047e27f10 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -1,102 +1,99 @@ - # 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) +# 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. _hledger_completion_function() { - #declare cmd=$1 - declare wordToComplete=$2 - declare precedingWord=$3 + # Current treatment for special characters: + # - exclude colon (:) from COMP_WORDBREAKS + # - use comptop -o filenames to escape the rest + COMP_WORDBREAKS=${COMP_WORDBREAKS//:} + compopt -o filenames - 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= + local wordToComplete=$2 + local subcommand + local subcommandOptions + local i + + for (( i=1; i<${#COMP_WORDS[@]}; i++ )); do + subcommand=${COMP_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 [[ $subcommand == "$wordToComplete" ]] && ((i == COMP_CWORD)); then + local subcommandMatches + subcommandMatches=$(grep -c "^$wordToComplete" <<< "$_hledger_complist_commands") + if ((subcommandMatches > 1)); then + subcommand= + break + else + _hledger_compreply "$subcommand" + return 0 + fi + fi + # Replace dashes with underscores and use indirect expansion + subcommandOptions=_hledger_complist_options_${subcommand//-/_} + _hledger_compreply "$(_hledger_compgen "${!subcommandOptions}")" + break done if [[ -z $subcommand ]]; then + _hledger_compreply_optarg && return - 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=' ' + # Completion lists are already sorted at build-time + # This keeps commands and options grouped separately + compopt -o nosort + _hledger_compreply "$(_hledger_compgen "$_hledger_complist_commands")" + _hledger_compreply_append "$(_hledger_compgen "$_hledger_complist_generic_options")" + return 0 fi + # Option argument completion after subcommand too + _hledger_compreply_optarg && return + + # Avoid setting compopt bellow if completing an option + [[ $wordToComplete == -* ]] && return + + # Almost all subcommands accept [QUERY] + # -> always add accounts to completion list + # Except for those few that will complain + local noQuery=(files help test) + [[ " ${noQuery[*]} " =~ " $subcommand " ]] && return + # Add any other subcommand special treatment here, or if it becomes unwieldy + # move it out in say _hledger_compreply_subcommand() and return on success. + + # Query specific completions + _hledger_compreply_query && return + + # Do not sort, keep accounts and query filters grouped separately + compopt -o nosort -o nospace + _hledger_compreply_append "$(_hledger_compgen "$_hledger_complist_query_filters")" + if [[ -z $wordToComplete ]]; then + _hledger_compreply_append "$(_hledger_compgen "$(_hledger accounts --flat --depth 1)")" + else + _hledger_compreply_append "$(_hledger_compgen "$(_hledger accounts --flat)")" + fi + + return 0 } _hledger_extension_completion_function() { - declare cmd=$1 + local cmd=$1 + shift # 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 + local extensionName=${cmd#*-} + COMP_WORDS=( "hledger" "$extensionName" "${COMP_WORDS[@]:1}" ) _hledger_completion_function "hledger" "$@" } @@ -107,79 +104,270 @@ complete -F _hledger_completion_function hledger complete -F _hledger_extension_completion_function hledger-ui complete -F _hledger_extension_completion_function hledger-web +# Helpers + +# Comment out when done +_hledger_debug() { + ((HLEDGER_DEBUG)) || return 0 + local var=${1:-COMP_WORDS} + printf '\ndebug: %s\n' "$(declare -p "$var")" >&2 +} + +# 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} +} + +_hledger_quote() +{ + local quoted + _hledger_quote_by_ref "$1" quoted + printf %s "$quoted" +} + +# Set the value of COMPREPLY from newline delimited completion candidates +_hledger_compreply() { + local IFS=$'\n' + COMPREPLY=($1) +} + +# Append the value of COMPREPLY from newline delimited completion candidates +_hledger_compreply_append() { + local IFS=$'\n' + 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 +# wordlist 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. +_hledger_compgen() { + local wordlist=$1 + local quoted=() + local word + local i=0 + + while IFS= read -r word; do + _hledger_quote_by_ref "$word" word + quoted[i++]=$word + done <<< "$wordlist" + + local IFS=$'\n' + compgen -W "${quoted[*]}" -- "$wordToComplete" +} + +# 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 optionIndex=${1:-$((COMP_CWORD - 1))} + local recursionLevel=${2:-0} + local wordlist + local error=0 + + case ${COMP_WORDS[optionIndex]} in + --alias) + compopt -o nospace + _hledger_compreply "$(_hledger_compgen "$(_hledger accounts --flat)")" + ;; + -f|--file|--rules-file|-o|--output-file) + _hledger_compreply "$(compgen -f -- "$wordToComplete")" + ;; + --pivot) + compopt -o nosort + wordlist="code description note payee" + _hledger_compreply "$(compgen -W "$wordlist" -- "$wordToComplete")" + _hledger_compreply_append "$(_hledger_compgen "$(_hledger tags)")" + ;; + --value) + wordlist="cost then end now" + _hledger_compreply "$(compgen -W "$wordlist" -- "$wordToComplete")" + ;; + -X|--exchange) + _hledger_compreply "$(_hledger_compgen "$(_hledger commodities)")" + ;; + --color|--colour) + compopt -o nosort + wordlist="auto always yes never no" + _hledger_compreply "$(compgen -W "$wordlist" -- "$wordToComplete")" + ;; + # Argument required, but no handler (yet) + -b|--begin|-e|--end|-p|--period|--depth) + _hledger_compreply "" + ;; + =) + # Recurse only once! + ((recursionLevel > 1)) && return 1 + if [[ ${COMP_WORDS[optionIndex - 1]} == -* ]]; then + _hledger_compreply_optarg $((optionIndex - 1)) $((recursionLevel + 1)) + error=$? + fi + ;; + *) + error=1 + ;; + esac + + return $error +} + +# Query filter completion through introspection +_hledger_compreply_query() { + [[ $wordToComplete =~ .: ]] || return + local query=${wordToComplete%%:*}: + grep -Fxqe "$query" <<< "$_hledger_complist_query_filters" || return + + local hledgerArgs=() + case $query in + acct:) + hledgerArgs=(accounts --flat) + ;; + code:) + hledgerArgs=(codes) + ;; + cur:) + hledgerArgs=(commodities) + ;; + desc:) + hledgerArgs=(descriptions) + ;; + note:) + hledgerArgs=(notes) + ;; + payee:) + hledgerArgs=(payees) + ;; + tag:) + hledgerArgs=(tags) + ;; + *) + return 1 + ;; + esac + + _hledger_compreply "$( + _hledger_compgen "$( + _hledger "${hledgerArgs[@]}" | sed "s/^/$query/g" + )" + )" + + return 0 +} + +# 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! +_hledger() { + local hledgerArgs=("$@") + local hledgerFile + local hledgerRulesFile + local i + + # hledger balance --file ~/ledger _ + # 0 1 2 3 4 + for (( i=1; i < ${#COMP_WORDS[@]} - 2; i++ )); do + case ${COMP_WORDS[i]} in + -f|--file) + if [[ ${COMP_WORDS[i+1]} == '=' ]]; then + hledgerFile=${COMP_WORDS[i+2]} + else + hledgerFile=${COMP_WORDS[i+1]} + fi + ;; + --rules-file) + if [[ ${COMP_WORDS[i+1]} == '=' ]]; then + hledgerRulesFile=${COMP_WORDS[i+2]} + else + hledgerRulesFile=${COMP_WORDS[i+1]} + fi + ;; + esac + done + + [[ -f $hledgerFile ]] && hledgerArgs+=(--file "$hledgerFile") + [[ -f $hledgerRulesFile ]] && hledgerArgs+=(--rules-file "$hledgerRulesFile") + + # 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" +read -r -d "" _hledger_complist_commands < "$_HLEDGER_COMPLETION_TEMPDIR/query-filters.txt" -not: +read -r -d "" _hledger_complist_query_filters < -amt:>= code: cur: -desc: date: date2: depth: +desc: +inacct: +not: note: payee: real: -real:0 status: -status:! -status:* tag: -inacct: TEXT -cat < "$_HLEDGER_COMPLETION_TEMPDIR/generic-options.txt" +read -r -d "" _hledger_complist_generic_options < "$_HLEDGER_COMPLETION_TEMPDIR/generic-options.txt" --version --weekly --yearly --B --C --D --E --I --M --N --P --Q --R --U --V --W --X --Y --b --e --f --h --p TEXT +# Dashes are replaced by m4 with underscores to form valid identifiers +# Referenced by indirect expansion of $subcommandOptions +read -r -d "" _hledger_complist_options_accounts < "$_HLEDGER_COMPLETION_TEMPDIR/options-add.txt" +read -r -d "" _hledger_complist_options_add < "$_HLEDGER_COMPLETION_TEMPDIR/options-add.txt" --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 ---strict ---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-check-dupes.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 ---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" +read -r -d "" _hledger_complist_options_areg < "$_HLEDGER_COMPLETION_TEMPDIR/options-aregister.txt" --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" +read -r -d "" _hledger_complist_options_aregister < "$_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 ---declared ---depth ---drop ---empty ---end ---exchange ---file ---flat ---forecast ---help ---ignore-assertions ---infer-market-price ---market ---monthly ---pending ---period ---pivot ---quarterly ---real ---rules-file ---tree ---unmarked ---used ---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 - -cat < "$_HLEDGER_COMPLETION_TEMPDIR/options-activity.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 +--txn-dates --unmarked --value --version --weekly +--width --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-balance.txt" +read -r -d "" _hledger_complist_options_bal < "$_HLEDGER_COMPLETION_TEMPDIR/options-balance.txt" --help --historical --ignore-assertions ---infer-market-price +--infer-value --invert --market --monthly @@ -1188,38 +632,414 @@ cat < "$_HLEDGER_COMPLETION_TEMPDIR/options-balance.txt" --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 -cat < "$_HLEDGER_COMPLETION_TEMPDIR/options-codes.txt" +read -r -d "" _hledger_complist_options_balance < "$_HLEDGER_COMPLETION_TEMPDIR/options-codes.txt" --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-commodities.txt" +read -r -d "" _hledger_complist_options_close < "$_HLEDGER_COMPLETION_TEMPDIR/options-commodities.txt" --pivot --rules-file --version --I --f --h TEXT -cat < "$_HLEDGER_COMPLETION_TEMPDIR/options-descriptions.txt" +read -r -d "" _hledger_complist_options_descriptions < "$_HLEDGER_COMPLETION_TEMPDIR/options-descriptions.txt" --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-files.txt" +read -r -d "" _hledger_complist_options_diff < "$_HLEDGER_COMPLETION_TEMPDIR/options-files.txt" --pivot --rules-file --version --I --f --h TEXT -cat < "$_HLEDGER_COMPLETION_TEMPDIR/options-notes.txt" +read -r -d "" _hledger_complist_options_equity < "$_HLEDGER_COMPLETION_TEMPDIR/options-notes.txt" --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-payees.txt" +read -r -d "" _hledger_complist_options_payees < "$_HLEDGER_COMPLETION_TEMPDIR/options-payees.txt" --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-prices.txt" +read -r -d "" _hledger_complist_options_prices < "$_HLEDGER_COMPLETION_TEMPDIR/options-prices.txt" --forecast --help --ignore-assertions ---infer-market-price +--infer-value --inverted-costs --market --monthly @@ -1509,29 +1534,9 @@ cat < "$_HLEDGER_COMPLETION_TEMPDIR/options-prices.txt" --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-print.txt" +read -r -d "" _hledger_complist_options_print < "$_HLEDGER_COMPLETION_TEMPDIR/options-print.txt" --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 -cat < "$_HLEDGER_COMPLETION_TEMPDIR/options-print-unique.txt" +read -r -d "" _hledger_complist_options_print_unique < "$_HLEDGER_COMPLETION_TEMPDIR/options-print-unique.txt" --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-register.txt" +read -r -d "" _hledger_complist_options_reg < "$_HLEDGER_COMPLETION_TEMPDIR/options-register.txt" --help --historical --ignore-assertions ---infer-market-price +--infer-value --invert --market --monthly @@ -1694,35 +1655,53 @@ cat < "$_HLEDGER_COMPLETION_TEMPDIR/options-register.txt" --weekly --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 -cat < "$_HLEDGER_COMPLETION_TEMPDIR/options-register-match.txt" +read -r -d "" _hledger_complist_options_register < "$_HLEDGER_COMPLETION_TEMPDIR/options-register-match.txt" --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-stats.txt" +read -r -d "" _hledger_complist_options_rewrite < "$_HLEDGER_COMPLETION_TEMPDIR/options-stats.txt" --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 -cat < "$_HLEDGER_COMPLETION_TEMPDIR/options-tags.txt" +read -r -d "" _hledger_complist_options_tags < "$_HLEDGER_COMPLETION_TEMPDIR/options-tags.txt" --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-test.txt" +read -r -d "" _hledger_complist_options_test < "$_HLEDGER_COMPLETION_TEMPDIR/options-help.txt" ---cat ---help ---info ---man ---pager --h -TEXT - -cat < "$_HLEDGER_COMPLETION_TEMPDIR/options-equity.txt" +read -r -d "" _hledger_complist_options_txns < "$_HLEDGER_COMPLETION_TEMPDIR/options-equity.txt" --help --ignore-assertions --infer-market-price ---interleaved --market +--match --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-areg.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 +--new --output-file --output-format --pending @@ -2012,360 +1931,14 @@ cat < "$_HLEDGER_COMPLETION_TEMPDIR/options-areg.txt" --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 ---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" +read -r -d "" _hledger_complist_options_ui < "$_HLEDGER_COMPLETION_TEMPDIR/options-ui.txt" --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 -cat < "$_HLEDGER_COMPLETION_TEMPDIR/options-web.txt" ---alias ---anon ---auto ---aux-date ---base-url ---begin ---capabilities ---capabilities-header ---cleared ---colour ---cors ---cost ---daily ---date2 ---debug ---depth ---empty ---end ---exchange ---file ---file-url ---forecast ---help ---host ---ignore-assertions ---infer-market-price ---market ---monthly ---pending ---period ---pivot ---port ---quarterly ---real ---rules-file ---serve ---server ---socket ---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 +read -r -d "" _hledger_complist_options_web < "$_HLEDGER_COMPLETION_TEMPDIR/options-api.txt" ---file ---help ---host ---port ---static-dir ---swagger ---version --d --f --h --p +read -r -d "" _hledger_complist_options_api < register, register-match, + # $subcommand == bal --> balance, balancesheet, balancesheetequity, etc. + # Do not ignore them! + if [[ $subcommand == "$wordToComplete" ]] && ((i == COMP_CWORD)); then + local subcommandMatches + subcommandMatches=$(grep -c "^$wordToComplete" <<< "$_hledger_complist_commands") + if ((subcommandMatches > 1)); then + subcommand= + break + else + _hledger_compreply "$subcommand" + return 0 + fi + fi + # Replace dashes with underscores and use indirect expansion + subcommandOptions=_hledger_complist_options_${subcommand//-/_} + _hledger_compreply "$(_hledger_compgen "${!subcommandOptions}")" + break done if [[ -z $subcommand ]]; then + _hledger_compreply_optarg && return - 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=' ' + # Completion lists are already sorted at build-time + # This keeps commands and options grouped separately + compopt -o nosort + _hledger_compreply "$(_hledger_compgen "$_hledger_complist_commands")" + _hledger_compreply_append "$(_hledger_compgen "$_hledger_complist_generic_options")" + return 0 fi + # Option argument completion after subcommand too + _hledger_compreply_optarg && return + + # Avoid setting compopt bellow if completing an option + [[ $wordToComplete == -* ]] && return + + # Almost all subcommands accept [QUERY] + # -> always add accounts to completion list + # Except for those few that will complain + local noQuery=(files help test) + [[ " ${noQuery[*]} " =~ " $subcommand " ]] && return + # Add any other subcommand special treatment here, or if it becomes unwieldy + # move it out in say _hledger_compreply_subcommand() and return on success. + + # Query specific completions + _hledger_compreply_query && return + + # Do not sort, keep accounts and query filters grouped separately + compopt -o nosort -o nospace + _hledger_compreply_append "$(_hledger_compgen "$_hledger_complist_query_filters")" + if [[ -z $wordToComplete ]]; then + _hledger_compreply_append "$(_hledger_compgen "$(_hledger accounts --flat --depth 1)")" + else + _hledger_compreply_append "$(_hledger_compgen "$(_hledger accounts --flat)")" + fi + + return 0 } _hledger_extension_completion_function() { - declare cmd=$1 + local cmd=$1 + shift # 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 + local extensionName=${cmd#*-} + COMP_WORDS=( "hledger" "$extensionName" "${COMP_WORDS[@]:1}" ) _hledger_completion_function "hledger" "$@" } @@ -107,26 +104,223 @@ complete -F _hledger_completion_function hledger complete -F _hledger_extension_completion_function hledger-ui complete -F _hledger_extension_completion_function hledger-web +# Helpers + +# Comment out when done +_hledger_debug() { + ((HLEDGER_DEBUG)) || return 0 + local var=${1:-COMP_WORDS} + printf '\ndebug: %s\n' "$(declare -p "$var")" >&2 +} + +# 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} +} + +_hledger_quote() +{ + local quoted + _hledger_quote_by_ref "$1" quoted + printf %s "$quoted" +} + +# Set the value of COMPREPLY from newline delimited completion candidates +_hledger_compreply() { + local IFS=$'\n' + COMPREPLY=($1) +} + +# Append the value of COMPREPLY from newline delimited completion candidates +_hledger_compreply_append() { + local IFS=$'\n' + 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 +# wordlist 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. +_hledger_compgen() { + local wordlist=$1 + local quoted=() + local word + local i=0 + + while IFS= read -r word; do + _hledger_quote_by_ref "$word" word + quoted[i++]=$word + done <<< "$wordlist" + + local IFS=$'\n' + compgen -W "${quoted[*]}" -- "$wordToComplete" +} + +# 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 optionIndex=${1:-$((COMP_CWORD - 1))} + local recursionLevel=${2:-0} + local wordlist + local error=0 + + case ${COMP_WORDS[optionIndex]} in + --alias) + compopt -o nospace + _hledger_compreply "$(_hledger_compgen "$(_hledger accounts --flat)")" + ;; + -f|--file|--rules-file|-o|--output-file) + _hledger_compreply "$(compgen -f -- "$wordToComplete")" + ;; + --pivot) + compopt -o nosort + wordlist="code description note payee" + _hledger_compreply "$(compgen -W "$wordlist" -- "$wordToComplete")" + _hledger_compreply_append "$(_hledger_compgen "$(_hledger tags)")" + ;; + --value) + wordlist="cost then end now" + _hledger_compreply "$(compgen -W "$wordlist" -- "$wordToComplete")" + ;; + -X|--exchange) + _hledger_compreply "$(_hledger_compgen "$(_hledger commodities)")" + ;; + --color|--colour) + compopt -o nosort + wordlist="auto always yes never no" + _hledger_compreply "$(compgen -W "$wordlist" -- "$wordToComplete")" + ;; + # Argument required, but no handler (yet) + -b|--begin|-e|--end|-p|--period|--depth) + _hledger_compreply "" + ;; + =) + # Recurse only once! + ((recursionLevel > 1)) && return 1 + if [[ ${COMP_WORDS[optionIndex - 1]} == -* ]]; then + _hledger_compreply_optarg $((optionIndex - 1)) $((recursionLevel + 1)) + error=$? + fi + ;; + *) + error=1 + ;; + esac + + return $error +} + +# Query filter completion through introspection +_hledger_compreply_query() { + [[ $wordToComplete =~ .: ]] || return + local query=${wordToComplete%%:*}: + grep -Fxqe "$query" <<< "$_hledger_complist_query_filters" || return + + local hledgerArgs=() + case $query in + acct:) + hledgerArgs=(accounts --flat) + ;; + code:) + hledgerArgs=(codes) + ;; + cur:) + hledgerArgs=(commodities) + ;; + desc:) + hledgerArgs=(descriptions) + ;; + note:) + hledgerArgs=(notes) + ;; + payee:) + hledgerArgs=(payees) + ;; + tag:) + hledgerArgs=(tags) + ;; + *) + return 1 + ;; + esac + + _hledger_compreply "$( + _hledger_compgen "$( + _hledger "${hledgerArgs[@]}" | sed "s/^/$query/g" + )" + )" + + return 0 +} + +# 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! +_hledger() { + local hledgerArgs=("$@") + local hledgerFile + local hledgerRulesFile + local i + + # hledger balance --file ~/ledger _ + # 0 1 2 3 4 + for (( i=1; i < ${#COMP_WORDS[@]} - 2; i++ )); do + case ${COMP_WORDS[i]} in + -f|--file) + if [[ ${COMP_WORDS[i+1]} == '=' ]]; then + hledgerFile=${COMP_WORDS[i+2]} + else + hledgerFile=${COMP_WORDS[i+1]} + fi + ;; + --rules-file) + if [[ ${COMP_WORDS[i+1]} == '=' ]]; then + hledgerRulesFile=${COMP_WORDS[i+2]} + else + hledgerRulesFile=${COMP_WORDS[i+1]} + fi + ;; + esac + done + + [[ -f $hledgerFile ]] && hledgerArgs+=(--file "$hledgerFile") + [[ -f $hledgerRulesFile ]] && hledgerArgs+=(--rules-file "$hledgerRulesFile") + + # 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" +read -r -d "" _hledger_complist_commands < "$_HLEDGER_COMPLETION_TEMPDIR/query-filters.txt" +read -r -d "" _hledger_complist_query_filters < "$_HLEDGER_COMPLETION_TEMPDIR/generic-options.txt" +read -r -d "" _hledger_complist_generic_options < "$_HLEDGER_COMPLETION_TEMPDIR/options-cmd.txt" +read -r -d "" _hledger_complist_options_`'translit(cmd, -, _) < "$tmp" - # Do not output mistaken commands that start with a dash (e.g. -h) - sed -rn 's/^ ([-a-z]+).*/\1/gp' "$tmp" \ - | grep -v ^- + sed -rn 's/^\s+([a-z][-a-z]+)\s+.*/\1/p' "$tmp" - # Output single command aliases in parenthesis: + # Output 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) + sed -rn 's/^\s+[a-z][-a-z]+\s+\(([a-z][ ,a-z]+)\).*/\1/p' "$tmp" | + sed 's/\s*,\s*/\n/g' | + sed '/^.$/d' } main "$@" diff --git a/shell-completion/output-options.sh b/shell-completion/output-options.sh index 3123e416b..76642e24c 100755 --- a/shell-completion/output-options.sh +++ b/shell-completion/output-options.sh @@ -4,10 +4,16 @@ set -o errexit -o pipefail -o nounset main() { - declare tmp - tmp=$(mktemp) + declare tmpdir="_options.tmp" + declare tmp="${tmpdir}/${1:-generic}" + + mkdir -p "$tmpdir" cat > "$tmp" - sed -rn 's/.* (-[a-zA-Z0-9]).*/\1/gp' < "$tmp" + + # Do not propose single letter completions. It's not useful, it's noisy + # and it makes completion slower: + # Display all 200 possibilities? (y or n) + # 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. 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: From d98ff3b93dd9a4d24754a7b1cfb7e70def8ed7b9 Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Mon, 7 Dec 2020 07:36:26 +0100 Subject: [PATCH 02/82] Fix quoted/escaped file path handling in _hledger() When reading hledger (rules-)file from COMP_WORDS we need to unescape it first. For once compgen is doing exactly what we need :) --- shell-completion/hledger-completion.bash | 3 +++ shell-completion/hledger-completion.bash.m4 | 3 +++ 2 files changed, 6 insertions(+) diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index 047e27f10..26afac3ad 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -281,6 +281,8 @@ _hledger() { else hledgerFile=${COMP_WORDS[i+1]} fi + # Pass it through compgen to unescape it + hledgerFile=$(compgen -W "$hledgerFile") ;; --rules-file) if [[ ${COMP_WORDS[i+1]} == '=' ]]; then @@ -288,6 +290,7 @@ _hledger() { else hledgerRulesFile=${COMP_WORDS[i+1]} fi + hledgerRulesFile=$(compgen -W "$hledgerRulesFile") ;; esac done diff --git a/shell-completion/hledger-completion.bash.m4 b/shell-completion/hledger-completion.bash.m4 index f284b0e39..aeb95c5bd 100644 --- a/shell-completion/hledger-completion.bash.m4 +++ b/shell-completion/hledger-completion.bash.m4 @@ -281,6 +281,8 @@ _hledger() { else hledgerFile=${COMP_WORDS[i+1]} fi + # Pass it through compgen to unescape it + hledgerFile=$(compgen -W "$hledgerFile") ;; --rules-file) if [[ ${COMP_WORDS[i+1]} == '=' ]]; then @@ -288,6 +290,7 @@ _hledger() { else hledgerRulesFile=${COMP_WORDS[i+1]} fi + hledgerRulesFile=$(compgen -W "$hledgerRulesFile") ;; esac done From dee25d4811a9571ad58afda9df0110340597d4b7 Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Tue, 8 Dec 2020 07:09:36 +0100 Subject: [PATCH 03/82] Fix: pass *all* accounts and rules files to hledger calls _hledger_optarg() is used to parse the options on the command line and provide their arguments for context aware completion suggestions --- shell-completion/hledger-completion.bash | 57 ++++++++++++--------- shell-completion/hledger-completion.bash.m4 | 57 ++++++++++++--------- 2 files changed, 66 insertions(+), 48 deletions(-) diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index 26afac3ad..373a01464 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -263,40 +263,49 @@ _hledger_compreply_query() { return 0 } -# 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! -_hledger() { - local hledgerArgs=("$@") - local hledgerFile - local hledgerRulesFile - local i +# 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 < ${#COMP_WORDS[@]} - 2; i++ )); do - case ${COMP_WORDS[i]} in - -f|--file) + offset=0 + for j in "${!options[@]}"; do + if [[ ${COMP_WORDS[i]} == "${options[j]}" ]]; then if [[ ${COMP_WORDS[i+1]} == '=' ]]; then - hledgerFile=${COMP_WORDS[i+2]} + offset=2 else - hledgerFile=${COMP_WORDS[i+1]} + offset=1 fi # Pass it through compgen to unescape it - hledgerFile=$(compgen -W "$hledgerFile") - ;; - --rules-file) - if [[ ${COMP_WORDS[i+1]} == '=' ]]; then - hledgerRulesFile=${COMP_WORDS[i+2]} - else - hledgerRulesFile=${COMP_WORDS[i+1]} - fi - hledgerRulesFile=$(compgen -W "$hledgerRulesFile") - ;; - esac + optarg+=("$(compgen -W "${COMP_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 - [[ -f $hledgerFile ]] && hledgerArgs+=(--file "$hledgerFile") - [[ -f $hledgerRulesFile ]] && hledgerArgs+=(--rules-file "$hledgerRulesFile") + _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/hledger-completion.bash.m4 b/shell-completion/hledger-completion.bash.m4 index aeb95c5bd..b580173ad 100644 --- a/shell-completion/hledger-completion.bash.m4 +++ b/shell-completion/hledger-completion.bash.m4 @@ -263,40 +263,49 @@ _hledger_compreply_query() { return 0 } -# 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! -_hledger() { - local hledgerArgs=("$@") - local hledgerFile - local hledgerRulesFile - local i +# 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 < ${#COMP_WORDS[@]} - 2; i++ )); do - case ${COMP_WORDS[i]} in - -f|--file) + offset=0 + for j in "${!options[@]}"; do + if [[ ${COMP_WORDS[i]} == "${options[j]}" ]]; then if [[ ${COMP_WORDS[i+1]} == '=' ]]; then - hledgerFile=${COMP_WORDS[i+2]} + offset=2 else - hledgerFile=${COMP_WORDS[i+1]} + offset=1 fi # Pass it through compgen to unescape it - hledgerFile=$(compgen -W "$hledgerFile") - ;; - --rules-file) - if [[ ${COMP_WORDS[i+1]} == '=' ]]; then - hledgerRulesFile=${COMP_WORDS[i+2]} - else - hledgerRulesFile=${COMP_WORDS[i+1]} - fi - hledgerRulesFile=$(compgen -W "$hledgerRulesFile") - ;; - esac + optarg+=("$(compgen -W "${COMP_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 - [[ -f $hledgerFile ]] && hledgerArgs+=(--file "$hledgerFile") - [[ -f $hledgerRulesFile ]] && hledgerArgs+=(--rules-file "$hledgerRulesFile") + _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 From 812a525acfbfa00cf812cf89bca22b5c3fee4c7d Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Tue, 8 Dec 2020 07:20:18 +0100 Subject: [PATCH 04/82] Basic settings for major editors: prevent mixing tabs and spaces --- shell-completion/hledger-completion.bash | 6 ++++++ shell-completion/hledger-completion.bash.m4 | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index 373a01464..f82f30ef4 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -1997,3 +1997,9 @@ TEXT read -r -d "" _hledger_complist_options_api < Date: Tue, 8 Dec 2020 07:23:30 +0100 Subject: [PATCH 05/82] Query filter completion for amt, real and status; clean whitespace --- shell-completion/hledger-completion.bash | 39 +++++++++------------ shell-completion/hledger-completion.bash.m4 | 39 +++++++++------------ 2 files changed, 34 insertions(+), 44 deletions(-) diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index f82f30ef4..ba1c95591 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -228,29 +228,24 @@ _hledger_compreply_query() { local hledgerArgs=() case $query in - acct:) - hledgerArgs=(accounts --flat) - ;; - code:) - hledgerArgs=(codes) - ;; - cur:) - hledgerArgs=(commodities) - ;; - desc:) - hledgerArgs=(descriptions) - ;; - note:) - hledgerArgs=(notes) - ;; - payee:) - hledgerArgs=(payees) - ;; - tag:) - hledgerArgs=(tags) - ;; + acct:) hledgerArgs=(accounts --flat) ;; + code:) hledgerArgs=(codes) ;; + cur:) hledgerArgs=(commodities) ;; + desc:) hledgerArgs=(descriptions) ;; + note:) hledgerArgs=(notes) ;; + payee:) hledgerArgs=(payees) ;; + tag:) hledgerArgs=(tags) ;; *) - return 1 + local wordlist + case $query in + amt:) wordlist="< <= > >=" ;; + real:) wordlist="\ 0" ;; + status:) wordlist="\ * !" ;; + *) return 1 ;; + esac + _get_comp_words_by_ref -n '<=>' -c wordToComplete + _hledger_compreply "$(compgen -P "$query" -W "$wordlist" -- "${wordToComplete#*:}")" + return 0 ;; esac diff --git a/shell-completion/hledger-completion.bash.m4 b/shell-completion/hledger-completion.bash.m4 index fe3020b9f..bfc48b15b 100644 --- a/shell-completion/hledger-completion.bash.m4 +++ b/shell-completion/hledger-completion.bash.m4 @@ -228,29 +228,24 @@ _hledger_compreply_query() { local hledgerArgs=() case $query in - acct:) - hledgerArgs=(accounts --flat) - ;; - code:) - hledgerArgs=(codes) - ;; - cur:) - hledgerArgs=(commodities) - ;; - desc:) - hledgerArgs=(descriptions) - ;; - note:) - hledgerArgs=(notes) - ;; - payee:) - hledgerArgs=(payees) - ;; - tag:) - hledgerArgs=(tags) - ;; + acct:) hledgerArgs=(accounts --flat) ;; + code:) hledgerArgs=(codes) ;; + cur:) hledgerArgs=(commodities) ;; + desc:) hledgerArgs=(descriptions) ;; + note:) hledgerArgs=(notes) ;; + payee:) hledgerArgs=(payees) ;; + tag:) hledgerArgs=(tags) ;; *) - return 1 + local wordlist + case $query in + amt:) wordlist="< <= > >=" ;; + real:) wordlist="\ 0" ;; + status:) wordlist="\ * !" ;; + *) return 1 ;; + esac + _get_comp_words_by_ref -n '<=>' -c wordToComplete + _hledger_compreply "$(compgen -P "$query" -W "$wordlist" -- "${wordToComplete#*:}")" + return 0 ;; esac From 3706636a765351d6b341dfe3da383f78c471ef85 Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Tue, 8 Dec 2020 09:07:39 +0100 Subject: [PATCH 06/82] Fix or silence shellcheck warnings There are a couple of places where (un)quoting is intentionally skipped so make those explicit. --- shell-completion/hledger-completion.bash | 9 +++++++-- shell-completion/hledger-completion.bash.m4 | 9 +++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index ba1c95591..a834967e8 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -1,3 +1,5 @@ +# shellcheck disable=2034 + # Completion script for hledger. # Created using a Makefile and real hledger. @@ -67,6 +69,7 @@ _hledger_completion_function() { # -> always add accounts to completion list # Except for those few that will complain local noQuery=(files help test) + # shellcheck disable=2076 [[ " ${noQuery[*]} " =~ " $subcommand " ]] && return # Add any other subcommand special treatment here, or if it becomes unwieldy # move it out in say _hledger_compreply_subcommand() and return on success. @@ -119,12 +122,12 @@ _hledger_debug() { # `compgen' which requires its arguments quoted/escaped: _hledger_quote_by_ref() { - printf -v $2 %q "$1" + 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} + [[ ${!2} == \$* ]] && eval "$2=${!2}" } _hledger_quote() @@ -137,12 +140,14 @@ _hledger_quote() # 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) } diff --git a/shell-completion/hledger-completion.bash.m4 b/shell-completion/hledger-completion.bash.m4 index bfc48b15b..61ed03979 100644 --- a/shell-completion/hledger-completion.bash.m4 +++ b/shell-completion/hledger-completion.bash.m4 @@ -1,3 +1,5 @@ +# shellcheck disable=2034 + # Completion script for hledger. # Created using a Makefile and real hledger. @@ -67,6 +69,7 @@ _hledger_completion_function() { # -> always add accounts to completion list # Except for those few that will complain local noQuery=(files help test) + # shellcheck disable=2076 [[ " ${noQuery[*]} " =~ " $subcommand " ]] && return # Add any other subcommand special treatment here, or if it becomes unwieldy # move it out in say _hledger_compreply_subcommand() and return on success. @@ -119,12 +122,12 @@ _hledger_debug() { # `compgen' which requires its arguments quoted/escaped: _hledger_quote_by_ref() { - printf -v $2 %q "$1" + 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} + [[ ${!2} == \$* ]] && eval "$2=${!2}" } _hledger_quote() @@ -137,12 +140,14 @@ _hledger_quote() # 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) } From 10cc8b72b96899cc0f6374b6063028d1060c12a4 Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Wed, 9 Dec 2020 09:32:33 +0100 Subject: [PATCH 07/82] Use _init_completion() This handles a lot that we have to do manually otherwise. Without this we need to handle e.g. redirections to get completion for say: > hledger payees > Also because this function assumes that we use `cur`, `prev`, `words` and `cword` and sets them up for us, `wordToComplete`, `COMP_WORDS` and `COMP_CWORD` are renamed accordingly. Those names are pretty much hard-coded in bash completion so it is easier to follow the lead than go with custom variable names. --- shell-completion/hledger-completion.bash | 50 +++++++++++---------- shell-completion/hledger-completion.bash.m4 | 50 +++++++++++---------- 2 files changed, 52 insertions(+), 48 deletions(-) diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index a834967e8..4d035876b 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -9,19 +9,21 @@ # the rest of the session and completion for other programs. _hledger_completion_function() { + local cur prev words cword + _init_completion -n : || return 0 + # Current treatment for special characters: # - exclude colon (:) from COMP_WORDBREAKS # - use comptop -o filenames to escape the rest COMP_WORDBREAKS=${COMP_WORDBREAKS//:} compopt -o filenames - local wordToComplete=$2 local subcommand local subcommandOptions local i - for (( i=1; i<${#COMP_WORDS[@]}; i++ )); do - subcommand=${COMP_WORDS[i]} + for (( i=1; i<${#words[@]}; i++ )); do + subcommand=${words[i]} if ! grep -Fxqe "$subcommand" <<< "$_hledger_complist_commands"; then subcommand= continue @@ -30,9 +32,9 @@ _hledger_completion_function() { # $subcommand == reg --> register, register-match, # $subcommand == bal --> balance, balancesheet, balancesheetequity, etc. # Do not ignore them! - if [[ $subcommand == "$wordToComplete" ]] && ((i == COMP_CWORD)); then + if [[ $subcommand == "$cur" ]] && ((i == cword)); then local subcommandMatches - subcommandMatches=$(grep -c "^$wordToComplete" <<< "$_hledger_complist_commands") + subcommandMatches=$(grep -c "^$cur" <<< "$_hledger_complist_commands") if ((subcommandMatches > 1)); then subcommand= break @@ -63,7 +65,7 @@ _hledger_completion_function() { _hledger_compreply_optarg && return # Avoid setting compopt bellow if completing an option - [[ $wordToComplete == -* ]] && return + [[ $cur == -* ]] && return # Almost all subcommands accept [QUERY] # -> always add accounts to completion list @@ -80,7 +82,7 @@ _hledger_completion_function() { # Do not sort, keep accounts and query filters grouped separately compopt -o nosort -o nospace _hledger_compreply_append "$(_hledger_compgen "$_hledger_complist_query_filters")" - if [[ -z $wordToComplete ]]; then + if [[ -z $cur ]]; then _hledger_compreply_append "$(_hledger_compgen "$(_hledger accounts --flat --depth 1)")" else _hledger_compreply_append "$(_hledger_compgen "$(_hledger accounts --flat)")" @@ -112,7 +114,7 @@ complete -F _hledger_extension_completion_function hledger-web # Comment out when done _hledger_debug() { ((HLEDGER_DEBUG)) || return 0 - local var=${1:-COMP_WORDS} + local var=${1:-words} printf '\ndebug: %s\n' "$(declare -p "$var")" >&2 } @@ -168,34 +170,34 @@ _hledger_compgen() { done <<< "$wordlist" local IFS=$'\n' - compgen -W "${quoted[*]}" -- "$wordToComplete" + compgen -W "${quoted[*]}" -- "$cur" } # 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 optionIndex=${1:-$((COMP_CWORD - 1))} + local optionIndex=${1:-$((cword - 1))} local recursionLevel=${2:-0} local wordlist local error=0 - case ${COMP_WORDS[optionIndex]} in + case ${words[optionIndex]} in --alias) compopt -o nospace _hledger_compreply "$(_hledger_compgen "$(_hledger accounts --flat)")" ;; -f|--file|--rules-file|-o|--output-file) - _hledger_compreply "$(compgen -f -- "$wordToComplete")" + _hledger_compreply "$(compgen -f -- "$cur")" ;; --pivot) compopt -o nosort wordlist="code description note payee" - _hledger_compreply "$(compgen -W "$wordlist" -- "$wordToComplete")" + _hledger_compreply "$(compgen -W "$wordlist" -- "$cur")" _hledger_compreply_append "$(_hledger_compgen "$(_hledger tags)")" ;; --value) wordlist="cost then end now" - _hledger_compreply "$(compgen -W "$wordlist" -- "$wordToComplete")" + _hledger_compreply "$(compgen -W "$wordlist" -- "$cur")" ;; -X|--exchange) _hledger_compreply "$(_hledger_compgen "$(_hledger commodities)")" @@ -203,7 +205,7 @@ _hledger_compreply_optarg() { --color|--colour) compopt -o nosort wordlist="auto always yes never no" - _hledger_compreply "$(compgen -W "$wordlist" -- "$wordToComplete")" + _hledger_compreply "$(compgen -W "$wordlist" -- "$cur")" ;; # Argument required, but no handler (yet) -b|--begin|-e|--end|-p|--period|--depth) @@ -212,7 +214,7 @@ _hledger_compreply_optarg() { =) # Recurse only once! ((recursionLevel > 1)) && return 1 - if [[ ${COMP_WORDS[optionIndex - 1]} == -* ]]; then + if [[ ${words[optionIndex - 1]} == -* ]]; then _hledger_compreply_optarg $((optionIndex - 1)) $((recursionLevel + 1)) error=$? fi @@ -227,8 +229,8 @@ _hledger_compreply_optarg() { # Query filter completion through introspection _hledger_compreply_query() { - [[ $wordToComplete =~ .: ]] || return - local query=${wordToComplete%%:*}: + [[ $cur =~ .: ]] || return + local query=${cur%%:*}: grep -Fxqe "$query" <<< "$_hledger_complist_query_filters" || return local hledgerArgs=() @@ -248,8 +250,8 @@ _hledger_compreply_query() { status:) wordlist="\ * !" ;; *) return 1 ;; esac - _get_comp_words_by_ref -n '<=>' -c wordToComplete - _hledger_compreply "$(compgen -P "$query" -W "$wordlist" -- "${wordToComplete#*:}")" + _get_comp_words_by_ref -n '<=>' -c cur + _hledger_compreply "$(compgen -P "$query" -W "$wordlist" -- "${cur#*:}")" return 0 ;; esac @@ -272,17 +274,17 @@ _hledger_optarg() { # hledger balance --file ~/ledger _ # 0 1 2 3 4 - for (( i=1; i < ${#COMP_WORDS[@]} - 2; i++ )); do + for (( i=1; i < ${#words[@]} - 2; i++ )); do offset=0 for j in "${!options[@]}"; do - if [[ ${COMP_WORDS[i]} == "${options[j]}" ]]; then - if [[ ${COMP_WORDS[i+1]} == '=' ]]; then + 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 "${COMP_WORDS[i + offset]}")") + optarg+=("$(compgen -W "${words[i + offset]}")") fi done ((i += offset)) diff --git a/shell-completion/hledger-completion.bash.m4 b/shell-completion/hledger-completion.bash.m4 index 61ed03979..afce257b8 100644 --- a/shell-completion/hledger-completion.bash.m4 +++ b/shell-completion/hledger-completion.bash.m4 @@ -9,19 +9,21 @@ # the rest of the session and completion for other programs. _hledger_completion_function() { + local cur prev words cword + _init_completion -n : || return 0 + # Current treatment for special characters: # - exclude colon (:) from COMP_WORDBREAKS # - use comptop -o filenames to escape the rest COMP_WORDBREAKS=${COMP_WORDBREAKS//:} compopt -o filenames - local wordToComplete=$2 local subcommand local subcommandOptions local i - for (( i=1; i<${#COMP_WORDS[@]}; i++ )); do - subcommand=${COMP_WORDS[i]} + for (( i=1; i<${#words[@]}; i++ )); do + subcommand=${words[i]} if ! grep -Fxqe "$subcommand" <<< "$_hledger_complist_commands"; then subcommand= continue @@ -30,9 +32,9 @@ _hledger_completion_function() { # $subcommand == reg --> register, register-match, # $subcommand == bal --> balance, balancesheet, balancesheetequity, etc. # Do not ignore them! - if [[ $subcommand == "$wordToComplete" ]] && ((i == COMP_CWORD)); then + if [[ $subcommand == "$cur" ]] && ((i == cword)); then local subcommandMatches - subcommandMatches=$(grep -c "^$wordToComplete" <<< "$_hledger_complist_commands") + subcommandMatches=$(grep -c "^$cur" <<< "$_hledger_complist_commands") if ((subcommandMatches > 1)); then subcommand= break @@ -63,7 +65,7 @@ _hledger_completion_function() { _hledger_compreply_optarg && return # Avoid setting compopt bellow if completing an option - [[ $wordToComplete == -* ]] && return + [[ $cur == -* ]] && return # Almost all subcommands accept [QUERY] # -> always add accounts to completion list @@ -80,7 +82,7 @@ _hledger_completion_function() { # Do not sort, keep accounts and query filters grouped separately compopt -o nosort -o nospace _hledger_compreply_append "$(_hledger_compgen "$_hledger_complist_query_filters")" - if [[ -z $wordToComplete ]]; then + if [[ -z $cur ]]; then _hledger_compreply_append "$(_hledger_compgen "$(_hledger accounts --flat --depth 1)")" else _hledger_compreply_append "$(_hledger_compgen "$(_hledger accounts --flat)")" @@ -112,7 +114,7 @@ complete -F _hledger_extension_completion_function hledger-web # Comment out when done _hledger_debug() { ((HLEDGER_DEBUG)) || return 0 - local var=${1:-COMP_WORDS} + local var=${1:-words} printf '\ndebug: %s\n' "$(declare -p "$var")" >&2 } @@ -168,34 +170,34 @@ _hledger_compgen() { done <<< "$wordlist" local IFS=$'\n' - compgen -W "${quoted[*]}" -- "$wordToComplete" + compgen -W "${quoted[*]}" -- "$cur" } # 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 optionIndex=${1:-$((COMP_CWORD - 1))} + local optionIndex=${1:-$((cword - 1))} local recursionLevel=${2:-0} local wordlist local error=0 - case ${COMP_WORDS[optionIndex]} in + case ${words[optionIndex]} in --alias) compopt -o nospace _hledger_compreply "$(_hledger_compgen "$(_hledger accounts --flat)")" ;; -f|--file|--rules-file|-o|--output-file) - _hledger_compreply "$(compgen -f -- "$wordToComplete")" + _hledger_compreply "$(compgen -f -- "$cur")" ;; --pivot) compopt -o nosort wordlist="code description note payee" - _hledger_compreply "$(compgen -W "$wordlist" -- "$wordToComplete")" + _hledger_compreply "$(compgen -W "$wordlist" -- "$cur")" _hledger_compreply_append "$(_hledger_compgen "$(_hledger tags)")" ;; --value) wordlist="cost then end now" - _hledger_compreply "$(compgen -W "$wordlist" -- "$wordToComplete")" + _hledger_compreply "$(compgen -W "$wordlist" -- "$cur")" ;; -X|--exchange) _hledger_compreply "$(_hledger_compgen "$(_hledger commodities)")" @@ -203,7 +205,7 @@ _hledger_compreply_optarg() { --color|--colour) compopt -o nosort wordlist="auto always yes never no" - _hledger_compreply "$(compgen -W "$wordlist" -- "$wordToComplete")" + _hledger_compreply "$(compgen -W "$wordlist" -- "$cur")" ;; # Argument required, but no handler (yet) -b|--begin|-e|--end|-p|--period|--depth) @@ -212,7 +214,7 @@ _hledger_compreply_optarg() { =) # Recurse only once! ((recursionLevel > 1)) && return 1 - if [[ ${COMP_WORDS[optionIndex - 1]} == -* ]]; then + if [[ ${words[optionIndex - 1]} == -* ]]; then _hledger_compreply_optarg $((optionIndex - 1)) $((recursionLevel + 1)) error=$? fi @@ -227,8 +229,8 @@ _hledger_compreply_optarg() { # Query filter completion through introspection _hledger_compreply_query() { - [[ $wordToComplete =~ .: ]] || return - local query=${wordToComplete%%:*}: + [[ $cur =~ .: ]] || return + local query=${cur%%:*}: grep -Fxqe "$query" <<< "$_hledger_complist_query_filters" || return local hledgerArgs=() @@ -248,8 +250,8 @@ _hledger_compreply_query() { status:) wordlist="\ * !" ;; *) return 1 ;; esac - _get_comp_words_by_ref -n '<=>' -c wordToComplete - _hledger_compreply "$(compgen -P "$query" -W "$wordlist" -- "${wordToComplete#*:}")" + _get_comp_words_by_ref -n '<=>' -c cur + _hledger_compreply "$(compgen -P "$query" -W "$wordlist" -- "${cur#*:}")" return 0 ;; esac @@ -272,17 +274,17 @@ _hledger_optarg() { # hledger balance --file ~/ledger _ # 0 1 2 3 4 - for (( i=1; i < ${#COMP_WORDS[@]} - 2; i++ )); do + for (( i=1; i < ${#words[@]} - 2; i++ )); do offset=0 for j in "${!options[@]}"; do - if [[ ${COMP_WORDS[i]} == "${options[j]}" ]]; then - if [[ ${COMP_WORDS[i+1]} == '=' ]]; then + 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 "${COMP_WORDS[i + offset]}")") + optarg+=("$(compgen -W "${words[i + offset]}")") fi done ((i += offset)) From fc89340c93e1c6a44fbac7b0bbc1e7896416f6d3 Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Wed, 9 Dec 2020 12:23:09 +0100 Subject: [PATCH 08/82] Add optional arguments $prefix and $match to _hledger_compgen() This allows more flexibility when generating completion candidates and we don't need to resort to external help from `sed` or others. _hledger_compgen's arguments become: $1 -> $wordlist: a newline separated wordlist with completion cadidates $2 -> $prefix: (optional) a prefix string to add to generated completions $3 -> $match: (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. Of course you can also pass an empty string as $prefix and set $match to whatever you wish. --- shell-completion/hledger-completion.bash | 16 +++++++++++++--- shell-completion/hledger-completion.bash.m4 | 16 +++++++++++++--- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index 4d035876b..82e5cc870 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -158,8 +158,18 @@ _hledger_compreply_append() { # wordlist 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 wordlist 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 wordlist=$1 + local prefix=$2 + local match=$3 local quoted=() local word local i=0 @@ -170,7 +180,7 @@ _hledger_compgen() { done <<< "$wordlist" local IFS=$'\n' - compgen -W "${quoted[*]}" -- "$cur" + compgen -P "$prefix" -W "${quoted[*]}" -- "${match:-${cur:${#prefix}}}" } # Try required option argument completion. Set COMPREPLY and return 0 on @@ -258,8 +268,8 @@ _hledger_compreply_query() { _hledger_compreply "$( _hledger_compgen "$( - _hledger "${hledgerArgs[@]}" | sed "s/^/$query/g" - )" + _hledger "${hledgerArgs[@]}" + )" "$query" )" return 0 diff --git a/shell-completion/hledger-completion.bash.m4 b/shell-completion/hledger-completion.bash.m4 index afce257b8..7c5e36382 100644 --- a/shell-completion/hledger-completion.bash.m4 +++ b/shell-completion/hledger-completion.bash.m4 @@ -158,8 +158,18 @@ _hledger_compreply_append() { # wordlist 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 wordlist 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 wordlist=$1 + local prefix=$2 + local match=$3 local quoted=() local word local i=0 @@ -170,7 +180,7 @@ _hledger_compgen() { done <<< "$wordlist" local IFS=$'\n' - compgen -W "${quoted[*]}" -- "$cur" + compgen -P "$prefix" -W "${quoted[*]}" -- "${match:-${cur:${#prefix}}}" } # Try required option argument completion. Set COMPREPLY and return 0 on @@ -258,8 +268,8 @@ _hledger_compreply_query() { _hledger_compreply "$( _hledger_compgen "$( - _hledger "${hledgerArgs[@]}" | sed "s/^/$query/g" - )" + _hledger "${hledgerArgs[@]}" + )" "$query" )" return 0 From 689ad83668754d517d8b8016d838750857c3b1c9 Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Wed, 9 Dec 2020 13:08:34 +0100 Subject: [PATCH 09/82] More option argument completions Added handlers for: --output-format --close-acct --open-acct --debug Added --drop to blocking list --- shell-completion/hledger-completion.bash | 13 ++++++++++++- shell-completion/hledger-completion.bash.m4 | 13 ++++++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index 82e5cc870..a8560f5cf 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -217,8 +217,19 @@ _hledger_compreply_optarg() { wordlist="auto always yes never no" _hledger_compreply "$(compgen -W "$wordlist" -- "$cur")" ;; + -O|--output-format) + wordlist="txt csv json sql" + _hledger_compreply "$(compgen -W "$wordlist" -- "$cur")" + ;; + --close-acct|--open-acct) + compopt -o nospace + _hledger_compreply "$(_hledger_compgen "$(_hledger accounts --flat)")" + ;; + --debug) + _hledger_compreply "$(compgen -W "{1..9}" -- "$cur")" + ;; # Argument required, but no handler (yet) - -b|--begin|-e|--end|-p|--period|--depth) + -b|--begin|-e|--end|-p|--period|--depth|--drop) _hledger_compreply "" ;; =) diff --git a/shell-completion/hledger-completion.bash.m4 b/shell-completion/hledger-completion.bash.m4 index 7c5e36382..20e853fd4 100644 --- a/shell-completion/hledger-completion.bash.m4 +++ b/shell-completion/hledger-completion.bash.m4 @@ -217,8 +217,19 @@ _hledger_compreply_optarg() { wordlist="auto always yes never no" _hledger_compreply "$(compgen -W "$wordlist" -- "$cur")" ;; + -O|--output-format) + wordlist="txt csv json sql" + _hledger_compreply "$(compgen -W "$wordlist" -- "$cur")" + ;; + --close-acct|--open-acct) + compopt -o nospace + _hledger_compreply "$(_hledger_compgen "$(_hledger accounts --flat)")" + ;; + --debug) + _hledger_compreply "$(compgen -W "{1..9}" -- "$cur")" + ;; # Argument required, but no handler (yet) - -b|--begin|-e|--end|-p|--period|--depth) + -b|--begin|-e|--end|-p|--period|--depth|--drop) _hledger_compreply "" ;; =) From dfc8796b25c7bc322759c83d58e95c60ce7bdf42 Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Thu, 10 Dec 2020 06:46:05 +0100 Subject: [PATCH 10/82] Style: replace $cur with $subcommand It changes nothing but spells out intention more clearly --- shell-completion/hledger-completion.bash | 2 +- shell-completion/hledger-completion.bash.m4 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index a8560f5cf..c49151285 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -34,7 +34,7 @@ _hledger_completion_function() { # Do not ignore them! if [[ $subcommand == "$cur" ]] && ((i == cword)); then local subcommandMatches - subcommandMatches=$(grep -c "^$cur" <<< "$_hledger_complist_commands") + subcommandMatches=$(grep -c "^$subcommand" <<< "$_hledger_complist_commands") if ((subcommandMatches > 1)); then subcommand= break diff --git a/shell-completion/hledger-completion.bash.m4 b/shell-completion/hledger-completion.bash.m4 index 20e853fd4..45eca0418 100644 --- a/shell-completion/hledger-completion.bash.m4 +++ b/shell-completion/hledger-completion.bash.m4 @@ -34,7 +34,7 @@ _hledger_completion_function() { # Do not ignore them! if [[ $subcommand == "$cur" ]] && ((i == cword)); then local subcommandMatches - subcommandMatches=$(grep -c "^$cur" <<< "$_hledger_complist_commands") + subcommandMatches=$(grep -c "^$subcommand" <<< "$_hledger_complist_commands") if ((subcommandMatches > 1)); then subcommand= break From 9569cfc8f33b7ff5cc4245cd73be6cef66dbbfb2 Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Thu, 10 Dec 2020 06:49:22 +0100 Subject: [PATCH 11/82] Style: unset compopt filenames if completing command or option This is about the looks of the completion list -- if we have a directory with the name of a subcommand it will be presented with a trailing slash. This avoids that. --- shell-completion/hledger-completion.bash | 2 +- shell-completion/hledger-completion.bash.m4 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index c49151285..7ef0d4ada 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -54,7 +54,7 @@ _hledger_completion_function() { # Completion lists are already sorted at build-time # This keeps commands and options grouped separately - compopt -o nosort + compopt -o nosort +o filenames _hledger_compreply "$(_hledger_compgen "$_hledger_complist_commands")" _hledger_compreply_append "$(_hledger_compgen "$_hledger_complist_generic_options")" diff --git a/shell-completion/hledger-completion.bash.m4 b/shell-completion/hledger-completion.bash.m4 index 45eca0418..c907660a8 100644 --- a/shell-completion/hledger-completion.bash.m4 +++ b/shell-completion/hledger-completion.bash.m4 @@ -54,7 +54,7 @@ _hledger_completion_function() { # Completion lists are already sorted at build-time # This keeps commands and options grouped separately - compopt -o nosort + compopt -o nosort +o filenames _hledger_compreply "$(_hledger_compgen "$_hledger_complist_commands")" _hledger_compreply_append "$(_hledger_compgen "$_hledger_complist_generic_options")" From 357a78e7bf211005542c8ec58e2c2436aaeb7d0b Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Thu, 10 Dec 2020 08:33:56 +0100 Subject: [PATCH 12/82] Fix --long-opt= completion Commit #64282f3f broke that somehow so I need to force the match on an empty string after the equal sign, not the equal sign itself. I think that _init_completion() does a bit more than I need... --- shell-completion/hledger-completion.bash | 30 +++++++++++++-------- shell-completion/hledger-completion.bash.m4 | 30 +++++++++++++-------- 2 files changed, 38 insertions(+), 22 deletions(-) diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index 7ef0d4ada..72a549387 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -179,8 +179,12 @@ _hledger_compgen() { quoted[i++]=$word done <<< "$wordlist" + if (( $# < 3 )); then + match=${cur:${#prefix}} + fi + local IFS=$'\n' - compgen -P "$prefix" -W "${quoted[*]}" -- "${match:-${cur:${#prefix}}}" + compgen -P "$prefix" -W "${quoted[*]}" -- "$match" } # Try required option argument completion. Set COMPREPLY and return 0 on @@ -190,43 +194,47 @@ _hledger_compreply_optarg() { local recursionLevel=${2:-0} local wordlist local error=0 + local match + + # Match the empty string on --file= + [[ $cur == = ]] || match=$cur case ${words[optionIndex]} in --alias) compopt -o nospace - _hledger_compreply "$(_hledger_compgen "$(_hledger accounts --flat)")" + _hledger_compreply "$(_hledger_compgen "$(_hledger accounts --flat)" "" "$match")" ;; -f|--file|--rules-file|-o|--output-file) - _hledger_compreply "$(compgen -f -- "$cur")" + _hledger_compreply "$(compgen -f -- "$match")" ;; --pivot) compopt -o nosort wordlist="code description note payee" - _hledger_compreply "$(compgen -W "$wordlist" -- "$cur")" - _hledger_compreply_append "$(_hledger_compgen "$(_hledger tags)")" + _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" -- "$cur")" + _hledger_compreply "$(compgen -W "$wordlist" -- "$match")" ;; -X|--exchange) - _hledger_compreply "$(_hledger_compgen "$(_hledger commodities)")" + _hledger_compreply "$(_hledger_compgen "$(_hledger commodities)" "" "$match")" ;; --color|--colour) compopt -o nosort wordlist="auto always yes never no" - _hledger_compreply "$(compgen -W "$wordlist" -- "$cur")" + _hledger_compreply "$(compgen -W "$wordlist" -- "$match")" ;; -O|--output-format) wordlist="txt csv json sql" - _hledger_compreply "$(compgen -W "$wordlist" -- "$cur")" + _hledger_compreply "$(compgen -W "$wordlist" -- "$match")" ;; --close-acct|--open-acct) compopt -o nospace - _hledger_compreply "$(_hledger_compgen "$(_hledger accounts --flat)")" + _hledger_compreply "$(_hledger_compgen "$(_hledger accounts --flat)" "" "$match")" ;; --debug) - _hledger_compreply "$(compgen -W "{1..9}" -- "$cur")" + _hledger_compreply "$(compgen -W "{1..9}" -- "$match")" ;; # Argument required, but no handler (yet) -b|--begin|-e|--end|-p|--period|--depth|--drop) diff --git a/shell-completion/hledger-completion.bash.m4 b/shell-completion/hledger-completion.bash.m4 index c907660a8..dda8ed86f 100644 --- a/shell-completion/hledger-completion.bash.m4 +++ b/shell-completion/hledger-completion.bash.m4 @@ -179,8 +179,12 @@ _hledger_compgen() { quoted[i++]=$word done <<< "$wordlist" + if (( $# < 3 )); then + match=${cur:${#prefix}} + fi + local IFS=$'\n' - compgen -P "$prefix" -W "${quoted[*]}" -- "${match:-${cur:${#prefix}}}" + compgen -P "$prefix" -W "${quoted[*]}" -- "$match" } # Try required option argument completion. Set COMPREPLY and return 0 on @@ -190,43 +194,47 @@ _hledger_compreply_optarg() { local recursionLevel=${2:-0} local wordlist local error=0 + local match + + # Match the empty string on --file= + [[ $cur == = ]] || match=$cur case ${words[optionIndex]} in --alias) compopt -o nospace - _hledger_compreply "$(_hledger_compgen "$(_hledger accounts --flat)")" + _hledger_compreply "$(_hledger_compgen "$(_hledger accounts --flat)" "" "$match")" ;; -f|--file|--rules-file|-o|--output-file) - _hledger_compreply "$(compgen -f -- "$cur")" + _hledger_compreply "$(compgen -f -- "$match")" ;; --pivot) compopt -o nosort wordlist="code description note payee" - _hledger_compreply "$(compgen -W "$wordlist" -- "$cur")" - _hledger_compreply_append "$(_hledger_compgen "$(_hledger tags)")" + _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" -- "$cur")" + _hledger_compreply "$(compgen -W "$wordlist" -- "$match")" ;; -X|--exchange) - _hledger_compreply "$(_hledger_compgen "$(_hledger commodities)")" + _hledger_compreply "$(_hledger_compgen "$(_hledger commodities)" "" "$match")" ;; --color|--colour) compopt -o nosort wordlist="auto always yes never no" - _hledger_compreply "$(compgen -W "$wordlist" -- "$cur")" + _hledger_compreply "$(compgen -W "$wordlist" -- "$match")" ;; -O|--output-format) wordlist="txt csv json sql" - _hledger_compreply "$(compgen -W "$wordlist" -- "$cur")" + _hledger_compreply "$(compgen -W "$wordlist" -- "$match")" ;; --close-acct|--open-acct) compopt -o nospace - _hledger_compreply "$(_hledger_compgen "$(_hledger accounts --flat)")" + _hledger_compreply "$(_hledger_compgen "$(_hledger accounts --flat)" "" "$match")" ;; --debug) - _hledger_compreply "$(compgen -W "{1..9}" -- "$cur")" + _hledger_compreply "$(compgen -W "{1..9}" -- "$match")" ;; # Argument required, but no handler (yet) -b|--begin|-e|--end|-p|--period|--depth|--drop) From 9dc65e4ee5bf88f68a6beedabf8bd46302592be1 Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Thu, 10 Dec 2020 09:15:04 +0100 Subject: [PATCH 13/82] Helper _hledger_debug() now accepts any number of arguments --- shell-completion/hledger-completion.bash | 7 +++++-- shell-completion/hledger-completion.bash.m4 | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index 72a549387..fcefa4ccc 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -114,8 +114,11 @@ complete -F _hledger_extension_completion_function hledger-web # Comment out when done _hledger_debug() { ((HLEDGER_DEBUG)) || return 0 - local var=${1:-words} - printf '\ndebug: %s\n' "$(declare -p "$var")" >&2 + local var vars=(words) + (($#)) && vars=("$@") + for var in "${vars[@]}"; do + printf '\ndebug: %s\n' "$(declare -p "$var")" >&2 + done } # Stolen from bash-completion diff --git a/shell-completion/hledger-completion.bash.m4 b/shell-completion/hledger-completion.bash.m4 index dda8ed86f..03eb32e0e 100644 --- a/shell-completion/hledger-completion.bash.m4 +++ b/shell-completion/hledger-completion.bash.m4 @@ -114,8 +114,11 @@ complete -F _hledger_extension_completion_function hledger-web # Comment out when done _hledger_debug() { ((HLEDGER_DEBUG)) || return 0 - local var=${1:-words} - printf '\ndebug: %s\n' "$(declare -p "$var")" >&2 + local var vars=(words) + (($#)) && vars=("$@") + for var in "${vars[@]}"; do + printf '\ndebug: %s\n' "$(declare -p "$var")" >&2 + done } # Stolen from bash-completion From 4101fdf3bb1b39fa329d780fcc2b5af3f579c472 Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Thu, 10 Dec 2020 11:38:07 +0100 Subject: [PATCH 14/82] Fix duplicate call of _hledger_compreply_optarg() It was called in both conditional branches so it's moved out before the conditional. --- shell-completion/hledger-completion.bash | 8 +++----- shell-completion/hledger-completion.bash.m4 | 8 +++----- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index fcefa4ccc..732f2e148 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -49,9 +49,10 @@ _hledger_completion_function() { break done - if [[ -z $subcommand ]]; then - _hledger_compreply_optarg && return + # Option argument completion + _hledger_compreply_optarg && return + if [[ -z $subcommand ]]; then # Completion lists are already sorted at build-time # This keeps commands and options grouped separately compopt -o nosort +o filenames @@ -61,9 +62,6 @@ _hledger_completion_function() { return 0 fi - # Option argument completion after subcommand too - _hledger_compreply_optarg && return - # Avoid setting compopt bellow if completing an option [[ $cur == -* ]] && return diff --git a/shell-completion/hledger-completion.bash.m4 b/shell-completion/hledger-completion.bash.m4 index 03eb32e0e..c63e06007 100644 --- a/shell-completion/hledger-completion.bash.m4 +++ b/shell-completion/hledger-completion.bash.m4 @@ -49,9 +49,10 @@ _hledger_completion_function() { break done - if [[ -z $subcommand ]]; then - _hledger_compreply_optarg && return + # Option argument completion + _hledger_compreply_optarg && return + if [[ -z $subcommand ]]; then # Completion lists are already sorted at build-time # This keeps commands and options grouped separately compopt -o nosort +o filenames @@ -61,9 +62,6 @@ _hledger_completion_function() { return 0 fi - # Option argument completion after subcommand too - _hledger_compreply_optarg && return - # Avoid setting compopt bellow if completing an option [[ $cur == -* ]] && return From c2da8ac2d49d5e60b4eb76205c64983efafce4b7 Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Thu, 10 Dec 2020 17:17:58 +0100 Subject: [PATCH 15/82] Fix extension completion When inserting $extensionName in $COMP_WORDS manually, $COMP_CWORD lags behind by one. Needs a manual adjustment too. --- shell-completion/hledger-completion.bash | 1 + shell-completion/hledger-completion.bash.m4 | 1 + 2 files changed, 2 insertions(+) diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index 732f2e148..a0542aca4 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -97,6 +97,7 @@ _hledger_extension_completion_function() { # normal hledger completion function. local extensionName=${cmd#*-} COMP_WORDS=( "hledger" "$extensionName" "${COMP_WORDS[@]:1}" ) + COMP_CWORD=$((COMP_CWORD + 1)) _hledger_completion_function "hledger" "$@" } diff --git a/shell-completion/hledger-completion.bash.m4 b/shell-completion/hledger-completion.bash.m4 index c63e06007..7ae4ae850 100644 --- a/shell-completion/hledger-completion.bash.m4 +++ b/shell-completion/hledger-completion.bash.m4 @@ -97,6 +97,7 @@ _hledger_extension_completion_function() { # normal hledger completion function. local extensionName=${cmd#*-} COMP_WORDS=( "hledger" "$extensionName" "${COMP_WORDS[@]:1}" ) + COMP_CWORD=$((COMP_CWORD + 1)) _hledger_completion_function "hledger" "$@" } From c72970834315f24dcbbadb0ca1016924e3571f7a Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Fri, 11 Dec 2020 07:20:12 +0100 Subject: [PATCH 16/82] Refactor _hledger_extension_completion_function() Reduce number of instructions, remove variables used only once --- shell-completion/hledger-completion.bash | 8 ++------ shell-completion/hledger-completion.bash.m4 | 8 ++------ 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index a0542aca4..0b627ca1a 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -90,15 +90,11 @@ _hledger_completion_function() { } _hledger_extension_completion_function() { - local cmd=$1 - shift - # Change parameters and arguments and call the # normal hledger completion function. - local extensionName=${cmd#*-} - COMP_WORDS=( "hledger" "$extensionName" "${COMP_WORDS[@]:1}" ) + COMP_WORDS=("hledger" "${1#*-}" "${COMP_WORDS[@]:1}") COMP_CWORD=$((COMP_CWORD + 1)) - _hledger_completion_function "hledger" "$@" + _hledger_completion_function "hledger" "${@:1}" } # Register completion function for hledger: diff --git a/shell-completion/hledger-completion.bash.m4 b/shell-completion/hledger-completion.bash.m4 index 7ae4ae850..ed5aa7009 100644 --- a/shell-completion/hledger-completion.bash.m4 +++ b/shell-completion/hledger-completion.bash.m4 @@ -90,15 +90,11 @@ _hledger_completion_function() { } _hledger_extension_completion_function() { - local cmd=$1 - shift - # Change parameters and arguments and call the # normal hledger completion function. - local extensionName=${cmd#*-} - COMP_WORDS=( "hledger" "$extensionName" "${COMP_WORDS[@]:1}" ) + COMP_WORDS=("hledger" "${1#*-}" "${COMP_WORDS[@]:1}") COMP_CWORD=$((COMP_CWORD + 1)) - _hledger_completion_function "hledger" "$@" + _hledger_completion_function "hledger" "${@:1}" } # Register completion function for hledger: From 9d94f847810fd29487133c454c6f6351cf76d13c Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Fri, 11 Dec 2020 08:20:50 +0100 Subject: [PATCH 17/82] Clarify comment in extension completion function --- shell-completion/hledger-completion.bash | 4 ++-- shell-completion/hledger-completion.bash.m4 | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index 0b627ca1a..967a8aa59 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -90,8 +90,8 @@ _hledger_completion_function() { } _hledger_extension_completion_function() { - # Change parameters and arguments and call the - # normal hledger completion function. + # Pretend that hledger is called with given extension + # as first argument and call main completion function COMP_WORDS=("hledger" "${1#*-}" "${COMP_WORDS[@]:1}") COMP_CWORD=$((COMP_CWORD + 1)) _hledger_completion_function "hledger" "${@:1}" diff --git a/shell-completion/hledger-completion.bash.m4 b/shell-completion/hledger-completion.bash.m4 index ed5aa7009..21b406e30 100644 --- a/shell-completion/hledger-completion.bash.m4 +++ b/shell-completion/hledger-completion.bash.m4 @@ -90,8 +90,8 @@ _hledger_completion_function() { } _hledger_extension_completion_function() { - # Change parameters and arguments and call the - # normal hledger completion function. + # Pretend that hledger is called with given extension + # as first argument and call main completion function COMP_WORDS=("hledger" "${1#*-}" "${COMP_WORDS[@]:1}") COMP_CWORD=$((COMP_CWORD + 1)) _hledger_completion_function "hledger" "${@:1}" From d30913c6a83c08509478de2b9d8e60d640480a1e Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Fri, 11 Dec 2020 08:50:03 +0100 Subject: [PATCH 18/82] Refactor _hledger_compreply_optarg() It used to do a lot more work parsing the command line that is no longer necessary. Clean up redundant code. --- shell-completion/hledger-completion.bash | 20 ++++---------------- shell-completion/hledger-completion.bash.m4 | 20 ++++---------------- 2 files changed, 8 insertions(+), 32 deletions(-) diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index 967a8aa59..e5fa7e2a6 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -188,16 +188,12 @@ _hledger_compgen() { # 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 optionIndex=${1:-$((cword - 1))} - local recursionLevel=${2:-0} - local wordlist - local error=0 - local match + local match wordlist # Match the empty string on --file= [[ $cur == = ]] || match=$cur - case ${words[optionIndex]} in + case $prev in --alias) compopt -o nospace _hledger_compreply "$(_hledger_compgen "$(_hledger accounts --flat)" "" "$match")" @@ -238,20 +234,12 @@ _hledger_compreply_optarg() { -b|--begin|-e|--end|-p|--period|--depth|--drop) _hledger_compreply "" ;; - =) - # Recurse only once! - ((recursionLevel > 1)) && return 1 - if [[ ${words[optionIndex - 1]} == -* ]]; then - _hledger_compreply_optarg $((optionIndex - 1)) $((recursionLevel + 1)) - error=$? - fi - ;; *) - error=1 + return 1 ;; esac - return $error + return 0 } # Query filter completion through introspection diff --git a/shell-completion/hledger-completion.bash.m4 b/shell-completion/hledger-completion.bash.m4 index 21b406e30..00948ccc1 100644 --- a/shell-completion/hledger-completion.bash.m4 +++ b/shell-completion/hledger-completion.bash.m4 @@ -188,16 +188,12 @@ _hledger_compgen() { # 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 optionIndex=${1:-$((cword - 1))} - local recursionLevel=${2:-0} - local wordlist - local error=0 - local match + local match wordlist # Match the empty string on --file= [[ $cur == = ]] || match=$cur - case ${words[optionIndex]} in + case $prev in --alias) compopt -o nospace _hledger_compreply "$(_hledger_compgen "$(_hledger accounts --flat)" "" "$match")" @@ -238,20 +234,12 @@ _hledger_compreply_optarg() { -b|--begin|-e|--end|-p|--period|--depth|--drop) _hledger_compreply "" ;; - =) - # Recurse only once! - ((recursionLevel > 1)) && return 1 - if [[ ${words[optionIndex - 1]} == -* ]]; then - _hledger_compreply_optarg $((optionIndex - 1)) $((recursionLevel + 1)) - error=$? - fi - ;; *) - error=1 + return 1 ;; esac - return $error + return 0 } # Query filter completion through introspection From b195826c0fa0bb3911fd559406cac16cd31cd46a Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Fri, 11 Dec 2020 08:57:28 +0100 Subject: [PATCH 19/82] Revert "Refactor _hledger_compreply_optarg()" This reverts commit 2fd01d8ef51b897a63a2590556dbb3af7c13ffc9. Well, I was so wrong... --- shell-completion/hledger-completion.bash | 20 ++++++++++++++++---- shell-completion/hledger-completion.bash.m4 | 20 ++++++++++++++++---- 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index e5fa7e2a6..967a8aa59 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -188,12 +188,16 @@ _hledger_compgen() { # 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 match wordlist + local optionIndex=${1:-$((cword - 1))} + local recursionLevel=${2:-0} + local wordlist + local error=0 + local match # Match the empty string on --file= [[ $cur == = ]] || match=$cur - case $prev in + case ${words[optionIndex]} in --alias) compopt -o nospace _hledger_compreply "$(_hledger_compgen "$(_hledger accounts --flat)" "" "$match")" @@ -234,12 +238,20 @@ _hledger_compreply_optarg() { -b|--begin|-e|--end|-p|--period|--depth|--drop) _hledger_compreply "" ;; + =) + # Recurse only once! + ((recursionLevel > 1)) && return 1 + if [[ ${words[optionIndex - 1]} == -* ]]; then + _hledger_compreply_optarg $((optionIndex - 1)) $((recursionLevel + 1)) + error=$? + fi + ;; *) - return 1 + error=1 ;; esac - return 0 + return $error } # Query filter completion through introspection diff --git a/shell-completion/hledger-completion.bash.m4 b/shell-completion/hledger-completion.bash.m4 index 00948ccc1..21b406e30 100644 --- a/shell-completion/hledger-completion.bash.m4 +++ b/shell-completion/hledger-completion.bash.m4 @@ -188,12 +188,16 @@ _hledger_compgen() { # 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 match wordlist + local optionIndex=${1:-$((cword - 1))} + local recursionLevel=${2:-0} + local wordlist + local error=0 + local match # Match the empty string on --file= [[ $cur == = ]] || match=$cur - case $prev in + case ${words[optionIndex]} in --alias) compopt -o nospace _hledger_compreply "$(_hledger_compgen "$(_hledger accounts --flat)" "" "$match")" @@ -234,12 +238,20 @@ _hledger_compreply_optarg() { -b|--begin|-e|--end|-p|--period|--depth|--drop) _hledger_compreply "" ;; + =) + # Recurse only once! + ((recursionLevel > 1)) && return 1 + if [[ ${words[optionIndex - 1]} == -* ]]; then + _hledger_compreply_optarg $((optionIndex - 1)) $((recursionLevel + 1)) + error=$? + fi + ;; *) - return 1 + error=1 ;; esac - return 0 + return $error } # Query filter completion through introspection From c139bb24a7053ba863a7d7960ed9a91c2d6e8b36 Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Fri, 11 Dec 2020 09:53:16 +0100 Subject: [PATCH 20/82] Minor refactor of main function No functional changes except special treatment of `help` subcommand --- shell-completion/hledger-completion.bash | 24 +++++++++++---------- shell-completion/hledger-completion.bash.m4 | 24 +++++++++++---------- 2 files changed, 26 insertions(+), 22 deletions(-) diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index 967a8aa59..9b0080a70 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -65,19 +65,21 @@ _hledger_completion_function() { # Avoid setting compopt bellow if completing an option [[ $cur == -* ]] && return - # Almost all subcommands accept [QUERY] - # -> always add accounts to completion list - # Except for those few that will complain - local noQuery=(files help test) - # shellcheck disable=2076 - [[ " ${noQuery[*]} " =~ " $subcommand " ]] && return - # Add any other subcommand special treatment here, or if it becomes unwieldy - # move it out in say _hledger_compreply_subcommand() and return on success. - - # Query specific completions + # Query completion _hledger_compreply_query && return - # Do not sort, keep accounts and query filters grouped separately + # Subcommand specific + case $subcommand in + files|test) return 0 ;; + help) + compopt -o nosort +o filenames + _hledger_compreply_append "$(compgen -W "$(hledger help | tail -n 1)" -- "$cur")" + return 0 + ;; + esac + + # Offer query filters and accounts for the rest + # Do not sort. Keep options, accounts and query filters grouped separately compopt -o nosort -o nospace _hledger_compreply_append "$(_hledger_compgen "$_hledger_complist_query_filters")" if [[ -z $cur ]]; then diff --git a/shell-completion/hledger-completion.bash.m4 b/shell-completion/hledger-completion.bash.m4 index 21b406e30..8b7ae9e43 100644 --- a/shell-completion/hledger-completion.bash.m4 +++ b/shell-completion/hledger-completion.bash.m4 @@ -65,19 +65,21 @@ _hledger_completion_function() { # Avoid setting compopt bellow if completing an option [[ $cur == -* ]] && return - # Almost all subcommands accept [QUERY] - # -> always add accounts to completion list - # Except for those few that will complain - local noQuery=(files help test) - # shellcheck disable=2076 - [[ " ${noQuery[*]} " =~ " $subcommand " ]] && return - # Add any other subcommand special treatment here, or if it becomes unwieldy - # move it out in say _hledger_compreply_subcommand() and return on success. - - # Query specific completions + # Query completion _hledger_compreply_query && return - # Do not sort, keep accounts and query filters grouped separately + # Subcommand specific + case $subcommand in + files|test) return 0 ;; + help) + compopt -o nosort +o filenames + _hledger_compreply_append "$(compgen -W "$(hledger help | tail -n 1)" -- "$cur")" + return 0 + ;; + esac + + # Offer query filters and accounts for the rest + # Do not sort. Keep options, accounts and query filters grouped separately compopt -o nosort -o nospace _hledger_compreply_append "$(_hledger_compgen "$_hledger_complist_query_filters")" if [[ -z $cur ]]; then From 57c79d0050e4b3cb052d01fb97d748fe06afcef9 Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Fri, 11 Dec 2020 10:11:43 +0100 Subject: [PATCH 21/82] Simplify condition Knowing the cursor position is enough to decide whether to complete subcommand or not, and subcommand is already known. --- shell-completion/hledger-completion.bash | 2 +- shell-completion/hledger-completion.bash.m4 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index 9b0080a70..3e14b677c 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -32,7 +32,7 @@ _hledger_completion_function() { # $subcommand == reg --> register, register-match, # $subcommand == bal --> balance, balancesheet, balancesheetequity, etc. # Do not ignore them! - if [[ $subcommand == "$cur" ]] && ((i == cword)); then + if ((i == cword)); then local subcommandMatches subcommandMatches=$(grep -c "^$subcommand" <<< "$_hledger_complist_commands") if ((subcommandMatches > 1)); then diff --git a/shell-completion/hledger-completion.bash.m4 b/shell-completion/hledger-completion.bash.m4 index 8b7ae9e43..d9bfe960d 100644 --- a/shell-completion/hledger-completion.bash.m4 +++ b/shell-completion/hledger-completion.bash.m4 @@ -32,7 +32,7 @@ _hledger_completion_function() { # $subcommand == reg --> register, register-match, # $subcommand == bal --> balance, balancesheet, balancesheetequity, etc. # Do not ignore them! - if [[ $subcommand == "$cur" ]] && ((i == cword)); then + if ((i == cword)); then local subcommandMatches subcommandMatches=$(grep -c "^$subcommand" <<< "$_hledger_complist_commands") if ((subcommandMatches > 1)); then From 985f041d817df3445bc0e07740ee70ed3c3e3e6a Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Fri, 11 Dec 2020 10:25:24 +0100 Subject: [PATCH 22/82] Make hard-coded completion word lists easy to find A quick search for `wordlist=` should be enough to find hard-coded completions if we need to change them --- shell-completion/hledger-completion.bash | 3 ++- shell-completion/hledger-completion.bash.m4 | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index 3e14b677c..d1c39123f 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -234,7 +234,8 @@ _hledger_compreply_optarg() { _hledger_compreply "$(_hledger_compgen "$(_hledger accounts --flat)" "" "$match")" ;; --debug) - _hledger_compreply "$(compgen -W "{1..9}" -- "$match")" + wordlist="{1..9}" + _hledger_compreply "$(compgen -W "$wordlist" -- "$match")" ;; # Argument required, but no handler (yet) -b|--begin|-e|--end|-p|--period|--depth|--drop) diff --git a/shell-completion/hledger-completion.bash.m4 b/shell-completion/hledger-completion.bash.m4 index d9bfe960d..2dbcd8558 100644 --- a/shell-completion/hledger-completion.bash.m4 +++ b/shell-completion/hledger-completion.bash.m4 @@ -234,7 +234,8 @@ _hledger_compreply_optarg() { _hledger_compreply "$(_hledger_compgen "$(_hledger accounts --flat)" "" "$match")" ;; --debug) - _hledger_compreply "$(compgen -W "{1..9}" -- "$match")" + wordlist="{1..9}" + _hledger_compreply "$(compgen -W "$wordlist" -- "$match")" ;; # Argument required, but no handler (yet) -b|--begin|-e|--end|-p|--period|--depth|--drop) From 8c14dfb3ebf82dad12cae5eb8f140923c4250359 Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Fri, 11 Dec 2020 11:27:20 +0100 Subject: [PATCH 23/82] A non-recursive version of _hledger_compreply_optarg() Feels more streamlined and readable --- shell-completion/hledger-completion.bash | 27 +++++++++------------ shell-completion/hledger-completion.bash.m4 | 27 +++++++++------------ 2 files changed, 22 insertions(+), 32 deletions(-) diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index d1c39123f..855109025 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -190,14 +190,17 @@ _hledger_compgen() { # 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 optionIndex=${1:-$((cword - 1))} - local recursionLevel=${2:-0} + local optionIndex=$((cword - 1)) + local match=$cur local wordlist - local error=0 - local match - # Match the empty string on --file= - [[ $cur == = ]] || match=$cur + # 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 + optionIndex=$((cword - 2)) + fi case ${words[optionIndex]} in --alias) @@ -241,20 +244,12 @@ _hledger_compreply_optarg() { -b|--begin|-e|--end|-p|--period|--depth|--drop) _hledger_compreply "" ;; - =) - # Recurse only once! - ((recursionLevel > 1)) && return 1 - if [[ ${words[optionIndex - 1]} == -* ]]; then - _hledger_compreply_optarg $((optionIndex - 1)) $((recursionLevel + 1)) - error=$? - fi - ;; *) - error=1 + return 1 ;; esac - return $error + return 0 } # Query filter completion through introspection diff --git a/shell-completion/hledger-completion.bash.m4 b/shell-completion/hledger-completion.bash.m4 index 2dbcd8558..892e57c9a 100644 --- a/shell-completion/hledger-completion.bash.m4 +++ b/shell-completion/hledger-completion.bash.m4 @@ -190,14 +190,17 @@ _hledger_compgen() { # 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 optionIndex=${1:-$((cword - 1))} - local recursionLevel=${2:-0} + local optionIndex=$((cword - 1)) + local match=$cur local wordlist - local error=0 - local match - # Match the empty string on --file= - [[ $cur == = ]] || match=$cur + # 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 + optionIndex=$((cword - 2)) + fi case ${words[optionIndex]} in --alias) @@ -241,20 +244,12 @@ _hledger_compreply_optarg() { -b|--begin|-e|--end|-p|--period|--depth|--drop) _hledger_compreply "" ;; - =) - # Recurse only once! - ((recursionLevel > 1)) && return 1 - if [[ ${words[optionIndex - 1]} == -* ]]; then - _hledger_compreply_optarg $((optionIndex - 1)) $((recursionLevel + 1)) - error=$? - fi - ;; *) - error=1 + return 1 ;; esac - return $error + return 0 } # Query filter completion through introspection From d943fa613efaba2ed54801f4744e60c039b10d82 Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Fri, 11 Dec 2020 12:45:31 +0100 Subject: [PATCH 24/82] Make sure `=` is present in COMP_WORDBREAKS Currently option processing logic is based on the assumption that `=` is a word-breaking character, so make sure it is present in COMP_WORDBREAKS --- shell-completion/hledger-completion.bash | 5 +++++ shell-completion/hledger-completion.bash.m4 | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index 855109025..2a4e1731e 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -14,8 +14,13 @@ _hledger_completion_function() { # Current treatment for special characters: # - exclude colon (:) from COMP_WORDBREAKS + # - option processing assumes that `=` is in COMP_WORDBREAKS # - use comptop -o filenames to escape the rest COMP_WORDBREAKS=${COMP_WORDBREAKS//:} + case "$COMP_WORDBREAKS" in + *=*) : ;; + *) COMP_WORDBREAKS="$COMP_WORDBREAKS=" ;; + esac compopt -o filenames local subcommand diff --git a/shell-completion/hledger-completion.bash.m4 b/shell-completion/hledger-completion.bash.m4 index 892e57c9a..7a61a2a0c 100644 --- a/shell-completion/hledger-completion.bash.m4 +++ b/shell-completion/hledger-completion.bash.m4 @@ -14,8 +14,13 @@ _hledger_completion_function() { # Current treatment for special characters: # - exclude colon (:) from COMP_WORDBREAKS + # - option processing assumes that `=` is in COMP_WORDBREAKS # - use comptop -o filenames to escape the rest COMP_WORDBREAKS=${COMP_WORDBREAKS//:} + case "$COMP_WORDBREAKS" in + *=*) : ;; + *) COMP_WORDBREAKS="$COMP_WORDBREAKS=" ;; + esac compopt -o filenames local subcommand From e2cac96449103768bb50224dbd2febfaaf1ca917 Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Fri, 11 Dec 2020 12:51:33 +0100 Subject: [PATCH 25/82] Typo in comments --- shell-completion/hledger-completion.bash | 2 +- shell-completion/hledger-completion.bash.m4 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index 2a4e1731e..1de73b131 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -15,7 +15,7 @@ _hledger_completion_function() { # Current treatment for special characters: # - exclude colon (:) from COMP_WORDBREAKS # - option processing assumes that `=` is in COMP_WORDBREAKS - # - use comptop -o filenames to escape the rest + # - use compopt -o filenames to escape the rest COMP_WORDBREAKS=${COMP_WORDBREAKS//:} case "$COMP_WORDBREAKS" in *=*) : ;; diff --git a/shell-completion/hledger-completion.bash.m4 b/shell-completion/hledger-completion.bash.m4 index 7a61a2a0c..296038cc5 100644 --- a/shell-completion/hledger-completion.bash.m4 +++ b/shell-completion/hledger-completion.bash.m4 @@ -15,7 +15,7 @@ _hledger_completion_function() { # Current treatment for special characters: # - exclude colon (:) from COMP_WORDBREAKS # - option processing assumes that `=` is in COMP_WORDBREAKS - # - use comptop -o filenames to escape the rest + # - use compopt -o filenames to escape the rest COMP_WORDBREAKS=${COMP_WORDBREAKS//:} case "$COMP_WORDBREAKS" in *=*) : ;; From ddf55a86a4468f6fed3dcdf55da22951aef6184f Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Fri, 11 Dec 2020 14:25:22 +0100 Subject: [PATCH 26/82] Rename $wordlist to $complist in _hledger_compgen() Keep to established conventions --- shell-completion/hledger-completion.bash | 8 ++++---- shell-completion/hledger-completion.bash.m4 | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index 1de73b131..f56a805b0 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -160,19 +160,19 @@ _hledger_compreply_append() { # Generate input suitable for _hledger_compreply() from newline delimited # completion candidates. It doesn't seem there is a way to feed a literal -# wordlist to compgen -- it will eat your quotes, drink your booze and... +# 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 wordlist with completion cadidates +# $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 wordlist=$1 + local complist=$1 local prefix=$2 local match=$3 local quoted=() @@ -182,7 +182,7 @@ _hledger_compgen() { while IFS= read -r word; do _hledger_quote_by_ref "$word" word quoted[i++]=$word - done <<< "$wordlist" + done <<< "$complist" if (( $# < 3 )); then match=${cur:${#prefix}} diff --git a/shell-completion/hledger-completion.bash.m4 b/shell-completion/hledger-completion.bash.m4 index 296038cc5..a94a1d124 100644 --- a/shell-completion/hledger-completion.bash.m4 +++ b/shell-completion/hledger-completion.bash.m4 @@ -160,19 +160,19 @@ _hledger_compreply_append() { # Generate input suitable for _hledger_compreply() from newline delimited # completion candidates. It doesn't seem there is a way to feed a literal -# wordlist to compgen -- it will eat your quotes, drink your booze and... +# 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 wordlist with completion cadidates +# $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 wordlist=$1 + local complist=$1 local prefix=$2 local match=$3 local quoted=() @@ -182,7 +182,7 @@ _hledger_compgen() { while IFS= read -r word; do _hledger_quote_by_ref "$word" word quoted[i++]=$word - done <<< "$wordlist" + done <<< "$complist" if (( $# < 3 )); then match=${cur:${#prefix}} From e6d54f79d77dd3af2725821971725fe93556f210 Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Sat, 12 Dec 2020 06:43:06 +0100 Subject: [PATCH 27/82] Postpone options display until after entering a dash I was looking at how other programs that have an overwhelming number of sub-commands and options deal with completion, namely how git does it, and I liked the clean workflow not spitting every available option until asked for. Hledger's main workflow is: > hledger COMMAND QUERY so I have tried to reproduce this with this change. Options are of course still there, but not shown until you ask for them by entering a dash on the command line. Also, the `acct:` filter proposes only top level accounts until there is some input from the user because accounts tend to be numerous as well. --- shell-completion/hledger-completion.bash | 28 ++++++++++++++------- shell-completion/hledger-completion.bash.m4 | 28 ++++++++++++++------- 2 files changed, 38 insertions(+), 18 deletions(-) diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index f56a805b0..35da09dd5 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -48,9 +48,11 @@ _hledger_completion_function() { return 0 fi fi - # Replace dashes with underscores and use indirect expansion - subcommandOptions=_hledger_complist_options_${subcommand//-/_} - _hledger_compreply "$(_hledger_compgen "${!subcommandOptions}")" + if [[ $cur == -* ]]; then + # Replace dashes with underscores and use indirect expansion + subcommandOptions=_hledger_complist_options_${subcommand//-/_} + _hledger_compreply "$(_hledger_compgen "${!subcommandOptions}")" + fi break done @@ -58,11 +60,12 @@ _hledger_completion_function() { _hledger_compreply_optarg && return if [[ -z $subcommand ]]; then - # Completion lists are already sorted at build-time - # This keeps commands and options grouped separately - compopt -o nosort +o filenames - _hledger_compreply "$(_hledger_compgen "$_hledger_complist_commands")" - _hledger_compreply_append "$(_hledger_compgen "$_hledger_complist_generic_options")" + compopt +o filenames + if [[ $cur == -* ]]; then + _hledger_compreply "$(_hledger_compgen "$_hledger_complist_generic_options")" + else + _hledger_compreply "$(_hledger_compgen "$_hledger_complist_commands")" + fi return 0 fi @@ -261,11 +264,18 @@ _hledger_compreply_optarg() { _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:) hledgerArgs=(accounts --flat) ;; + acct:) + if (( ${#match} )); then + hledgerArgs=(accounts --flat) + else + hledgerArgs=(accounts --flat --depth 1) + fi + ;; code:) hledgerArgs=(codes) ;; cur:) hledgerArgs=(commodities) ;; desc:) hledgerArgs=(descriptions) ;; diff --git a/shell-completion/hledger-completion.bash.m4 b/shell-completion/hledger-completion.bash.m4 index a94a1d124..5c3ab27c4 100644 --- a/shell-completion/hledger-completion.bash.m4 +++ b/shell-completion/hledger-completion.bash.m4 @@ -48,9 +48,11 @@ _hledger_completion_function() { return 0 fi fi - # Replace dashes with underscores and use indirect expansion - subcommandOptions=_hledger_complist_options_${subcommand//-/_} - _hledger_compreply "$(_hledger_compgen "${!subcommandOptions}")" + if [[ $cur == -* ]]; then + # Replace dashes with underscores and use indirect expansion + subcommandOptions=_hledger_complist_options_${subcommand//-/_} + _hledger_compreply "$(_hledger_compgen "${!subcommandOptions}")" + fi break done @@ -58,11 +60,12 @@ _hledger_completion_function() { _hledger_compreply_optarg && return if [[ -z $subcommand ]]; then - # Completion lists are already sorted at build-time - # This keeps commands and options grouped separately - compopt -o nosort +o filenames - _hledger_compreply "$(_hledger_compgen "$_hledger_complist_commands")" - _hledger_compreply_append "$(_hledger_compgen "$_hledger_complist_generic_options")" + compopt +o filenames + if [[ $cur == -* ]]; then + _hledger_compreply "$(_hledger_compgen "$_hledger_complist_generic_options")" + else + _hledger_compreply "$(_hledger_compgen "$_hledger_complist_commands")" + fi return 0 fi @@ -261,11 +264,18 @@ _hledger_compreply_optarg() { _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:) hledgerArgs=(accounts --flat) ;; + acct:) + if (( ${#match} )); then + hledgerArgs=(accounts --flat) + else + hledgerArgs=(accounts --flat --depth 1) + fi + ;; code:) hledgerArgs=(codes) ;; cur:) hledgerArgs=(commodities) ;; desc:) hledgerArgs=(descriptions) ;; From 09132ace80b1e364d1622765dbdf451378fcf80e Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Sat, 12 Dec 2020 07:43:12 +0100 Subject: [PATCH 28/82] f656ff8 made another early return possible, so do it Also it made a couple of statements redundant, cleaned up. --- shell-completion/hledger-completion.bash | 8 +++----- shell-completion/hledger-completion.bash.m4 | 8 +++----- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index 35da09dd5..b8b678548 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -52,6 +52,7 @@ _hledger_completion_function() { # Replace dashes with underscores and use indirect expansion subcommandOptions=_hledger_complist_options_${subcommand//-/_} _hledger_compreply "$(_hledger_compgen "${!subcommandOptions}")" + return 0 fi break done @@ -70,9 +71,6 @@ _hledger_completion_function() { return 0 fi - # Avoid setting compopt bellow if completing an option - [[ $cur == -* ]] && return - # Query completion _hledger_compreply_query && return @@ -81,7 +79,7 @@ _hledger_completion_function() { files|test) return 0 ;; help) compopt -o nosort +o filenames - _hledger_compreply_append "$(compgen -W "$(hledger help | tail -n 1)" -- "$cur")" + _hledger_compreply "$(compgen -W "$(hledger help | tail -n 1)" -- "$cur")" return 0 ;; esac @@ -89,7 +87,7 @@ _hledger_completion_function() { # Offer query filters and accounts for the rest # Do not sort. Keep options, accounts and query filters grouped separately compopt -o nosort -o nospace - _hledger_compreply_append "$(_hledger_compgen "$_hledger_complist_query_filters")" + _hledger_compreply "$(_hledger_compgen "$_hledger_complist_query_filters")" if [[ -z $cur ]]; then _hledger_compreply_append "$(_hledger_compgen "$(_hledger accounts --flat --depth 1)")" else diff --git a/shell-completion/hledger-completion.bash.m4 b/shell-completion/hledger-completion.bash.m4 index 5c3ab27c4..02b1d36bc 100644 --- a/shell-completion/hledger-completion.bash.m4 +++ b/shell-completion/hledger-completion.bash.m4 @@ -52,6 +52,7 @@ _hledger_completion_function() { # Replace dashes with underscores and use indirect expansion subcommandOptions=_hledger_complist_options_${subcommand//-/_} _hledger_compreply "$(_hledger_compgen "${!subcommandOptions}")" + return 0 fi break done @@ -70,9 +71,6 @@ _hledger_completion_function() { return 0 fi - # Avoid setting compopt bellow if completing an option - [[ $cur == -* ]] && return - # Query completion _hledger_compreply_query && return @@ -81,7 +79,7 @@ _hledger_completion_function() { files|test) return 0 ;; help) compopt -o nosort +o filenames - _hledger_compreply_append "$(compgen -W "$(hledger help | tail -n 1)" -- "$cur")" + _hledger_compreply "$(compgen -W "$(hledger help | tail -n 1)" -- "$cur")" return 0 ;; esac @@ -89,7 +87,7 @@ _hledger_completion_function() { # Offer query filters and accounts for the rest # Do not sort. Keep options, accounts and query filters grouped separately compopt -o nosort -o nospace - _hledger_compreply_append "$(_hledger_compgen "$_hledger_complist_query_filters")" + _hledger_compreply "$(_hledger_compgen "$_hledger_complist_query_filters")" if [[ -z $cur ]]; then _hledger_compreply_append "$(_hledger_compgen "$(_hledger accounts --flat --depth 1)")" else From 8feb9053ec47dc3acd425e4f22cf9f669468999f Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Sat, 12 Dec 2020 10:47:15 +0100 Subject: [PATCH 29/82] Append `=` to long options requiring an argument Make it obvious that the option expects an argument by appending the equal sign on completion. Suspend space in this case. The regular expression in `output-options.sh` is adjusted to take into account this on processing option strings. --- shell-completion/hledger-completion.bash | 970 ++++++++++---------- shell-completion/hledger-completion.bash.m4 | 8 + shell-completion/output-options.sh | 2 +- 3 files changed, 509 insertions(+), 471 deletions(-) diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index b8b678548..fa4670e95 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -52,6 +52,11 @@ _hledger_completion_function() { # Replace dashes with underscores and use indirect expansion subcommandOptions=_hledger_complist_options_${subcommand//-/_} _hledger_compreply "$(_hledger_compgen "${!subcommandOptions}")" + + # Suspend space on completion of long options requiring an argument + [[ ${COMPREPLY[0]} == --*= ]] && compopt -o nospace + compopt +o filenames + return 0 fi break @@ -64,6 +69,9 @@ _hledger_completion_function() { compopt +o filenames 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 @@ -420,22 +428,22 @@ tag: TEXT read -r -d "" _hledger_complist_generic_options < Date: Sat, 12 Dec 2020 11:52:48 +0100 Subject: [PATCH 30/82] Adjust comment obsoleted by previous commit: 4d2a4b0 --- shell-completion/output-options.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shell-completion/output-options.sh b/shell-completion/output-options.sh index 80b47fb21..0239416e6 100755 --- a/shell-completion/output-options.sh +++ b/shell-completion/output-options.sh @@ -15,8 +15,8 @@ main() { # Display all 200 possibilities? (y or n) # 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. + # Options requiring an argument make that explicit by appending + # the equal sign (=) sed -rn 's/.* (--[a-zA-Z][-_a-zA-Z0-9]*=?).*/\1/gp' < "$tmp" } From a5ccae8f31fb3618dcc83f3db3f651e0a77315df Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Sat, 12 Dec 2020 13:01:29 +0100 Subject: [PATCH 31/82] Fix regular expression in output-options.sh Match only lines starting with white space because there are a lot of option like strings in the discussion section that follows that are not necessarily what we are after. --- shell-completion/hledger-completion.bash | 26 ------------------------ shell-completion/output-options.sh | 2 +- 2 files changed, 1 insertion(+), 27 deletions(-) diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index fa4670e95..b1bd5e562 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -479,9 +479,7 @@ read -r -d "" _hledger_complist_options_accounts < Date: Sat, 12 Dec 2020 13:31:36 +0100 Subject: [PATCH 32/82] Improve option extraction Do not consider lines starting with anything other than white space followed by a dash. --- shell-completion/hledger-completion.bash | 37 ------------------------ shell-completion/output-options.sh | 3 +- 2 files changed, 2 insertions(+), 38 deletions(-) diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index b1bd5e562..8fa684e9a 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -431,7 +431,6 @@ read -r -d "" _hledger_complist_generic_options < Date: Sat, 12 Dec 2020 15:47:30 +0100 Subject: [PATCH 33/82] Further refinement of option extraction regex --- shell-completion/hledger-completion.bash | 76 ++++++++++++------------ shell-completion/output-options.sh | 2 +- 2 files changed, 38 insertions(+), 40 deletions(-) diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index 8fa684e9a..fb5f55105 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -433,7 +433,7 @@ read -r -d "" _hledger_complist_generic_options < Date: Sun, 13 Dec 2020 06:44:08 +0100 Subject: [PATCH 34/82] Add a couple of sub-commands to the no-query list --- shell-completion/hledger-completion.bash | 5 ++++- shell-completion/hledger-completion.bash.m4 | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index fb5f55105..aaae936e5 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -84,12 +84,15 @@ _hledger_completion_function() { # Subcommand specific case $subcommand in - files|test) return 0 ;; help) compopt -o nosort +o filenames _hledger_compreply "$(compgen -W "$(hledger help | tail -n 1)" -- "$cur")" return 0 ;; + # These do not expect or support any query arguments + commodities|check-dupes|files|import|print-unique|test) + return 0 + ;; esac # Offer query filters and accounts for the rest diff --git a/shell-completion/hledger-completion.bash.m4 b/shell-completion/hledger-completion.bash.m4 index 06de7ce71..b77a9421d 100644 --- a/shell-completion/hledger-completion.bash.m4 +++ b/shell-completion/hledger-completion.bash.m4 @@ -84,12 +84,15 @@ _hledger_completion_function() { # Subcommand specific case $subcommand in - files|test) return 0 ;; help) compopt -o nosort +o filenames _hledger_compreply "$(compgen -W "$(hledger help | tail -n 1)" -- "$cur")" return 0 ;; + # These do not expect or support any query arguments + commodities|check-dupes|files|import|print-unique|test) + return 0 + ;; esac # Offer query filters and accounts for the rest From e2b2c2fb2e881262030a9786d2c9fa5e43329614 Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Sun, 13 Dec 2020 06:45:27 +0100 Subject: [PATCH 35/82] _hledger_compreply_query: minor refactor --- shell-completion/hledger-completion.bash | 2 +- shell-completion/hledger-completion.bash.m4 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index aaae936e5..2cd85e906 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -300,7 +300,7 @@ _hledger_compreply_query() { *) return 1 ;; esac _get_comp_words_by_ref -n '<=>' -c cur - _hledger_compreply "$(compgen -P "$query" -W "$wordlist" -- "${cur#*:}")" + _hledger_compreply "$(compgen -P "$query" -W "$wordlist" -- "$match")" return 0 ;; esac diff --git a/shell-completion/hledger-completion.bash.m4 b/shell-completion/hledger-completion.bash.m4 index b77a9421d..39375842b 100644 --- a/shell-completion/hledger-completion.bash.m4 +++ b/shell-completion/hledger-completion.bash.m4 @@ -300,7 +300,7 @@ _hledger_compreply_query() { *) return 1 ;; esac _get_comp_words_by_ref -n '<=>' -c cur - _hledger_compreply "$(compgen -P "$query" -W "$wordlist" -- "${cur#*:}")" + _hledger_compreply "$(compgen -P "$query" -W "$wordlist" -- "$match")" return 0 ;; esac From f5d2a6bcc35ed7f1bb469f79fe770a74403bb47d Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Sun, 13 Dec 2020 07:14:33 +0100 Subject: [PATCH 36/82] Move sub-command options reply out of the for-loop It was just before the `break` statement anyways --- shell-completion/hledger-completion.bash | 28 ++++++++++----------- shell-completion/hledger-completion.bash.m4 | 28 ++++++++++----------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index 2cd85e906..f53e385de 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -24,10 +24,8 @@ _hledger_completion_function() { compopt -o filenames local subcommand - local subcommandOptions local i - - for (( i=1; i<${#words[@]}; i++ )); do + for ((i=1; i<${#words[@]}; i++)); do subcommand=${words[i]} if ! grep -Fxqe "$subcommand" <<< "$_hledger_complist_commands"; then subcommand= @@ -48,17 +46,6 @@ _hledger_completion_function() { return 0 fi fi - if [[ $cur == -* ]]; then - # Replace dashes with underscores and use indirect expansion - subcommandOptions=_hledger_complist_options_${subcommand//-/_} - _hledger_compreply "$(_hledger_compgen "${!subcommandOptions}")" - - # Suspend space on completion of long options requiring an argument - [[ ${COMPREPLY[0]} == --*= ]] && compopt -o nospace - compopt +o filenames - - return 0 - fi break done @@ -79,6 +66,19 @@ _hledger_completion_function() { return 0 fi + if [[ $cur == -* ]]; then + local subcommandOptions + # Replace dashes with underscores and use indirect expansion + subcommandOptions=_hledger_complist_options_${subcommand//-/_} + _hledger_compreply "$(_hledger_compgen "${!subcommandOptions}")" + + # Suspend space on completion of long options requiring an argument + [[ ${COMPREPLY[0]} == --*= ]] && compopt -o nospace + compopt +o filenames + + return 0 + fi + # Query completion _hledger_compreply_query && return diff --git a/shell-completion/hledger-completion.bash.m4 b/shell-completion/hledger-completion.bash.m4 index 39375842b..2c30c8cf9 100644 --- a/shell-completion/hledger-completion.bash.m4 +++ b/shell-completion/hledger-completion.bash.m4 @@ -24,10 +24,8 @@ _hledger_completion_function() { compopt -o filenames local subcommand - local subcommandOptions local i - - for (( i=1; i<${#words[@]}; i++ )); do + for ((i=1; i<${#words[@]}; i++)); do subcommand=${words[i]} if ! grep -Fxqe "$subcommand" <<< "$_hledger_complist_commands"; then subcommand= @@ -48,17 +46,6 @@ _hledger_completion_function() { return 0 fi fi - if [[ $cur == -* ]]; then - # Replace dashes with underscores and use indirect expansion - subcommandOptions=_hledger_complist_options_${subcommand//-/_} - _hledger_compreply "$(_hledger_compgen "${!subcommandOptions}")" - - # Suspend space on completion of long options requiring an argument - [[ ${COMPREPLY[0]} == --*= ]] && compopt -o nospace - compopt +o filenames - - return 0 - fi break done @@ -79,6 +66,19 @@ _hledger_completion_function() { return 0 fi + if [[ $cur == -* ]]; then + local subcommandOptions + # Replace dashes with underscores and use indirect expansion + subcommandOptions=_hledger_complist_options_${subcommand//-/_} + _hledger_compreply "$(_hledger_compgen "${!subcommandOptions}")" + + # Suspend space on completion of long options requiring an argument + [[ ${COMPREPLY[0]} == --*= ]] && compopt -o nospace + compopt +o filenames + + return 0 + fi + # Query completion _hledger_compreply_query && return From 046421e712635e793a4057e1461c1083de5ee7cd Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Sun, 13 Dec 2020 07:43:29 +0100 Subject: [PATCH 37/82] Simplify sub-command loop logic If the cursor is on the sub-command, just offer sub-command completions and be done with it. It was way over complicated. --- shell-completion/hledger-completion.bash | 12 +++--------- shell-completion/hledger-completion.bash.m4 | 12 +++--------- 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index f53e385de..0b8ecd48c 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -36,15 +36,9 @@ _hledger_completion_function() { # $subcommand == bal --> balance, balancesheet, balancesheetequity, etc. # Do not ignore them! if ((i == cword)); then - local subcommandMatches - subcommandMatches=$(grep -c "^$subcommand" <<< "$_hledger_complist_commands") - if ((subcommandMatches > 1)); then - subcommand= - break - else - _hledger_compreply "$subcommand" - return 0 - fi + compopt +o filenames + _hledger_compreply "$(_hledger_compgen "$_hledger_complist_commands")" + return 0 fi break done diff --git a/shell-completion/hledger-completion.bash.m4 b/shell-completion/hledger-completion.bash.m4 index 2c30c8cf9..9bfc23a32 100644 --- a/shell-completion/hledger-completion.bash.m4 +++ b/shell-completion/hledger-completion.bash.m4 @@ -36,15 +36,9 @@ _hledger_completion_function() { # $subcommand == bal --> balance, balancesheet, balancesheetequity, etc. # Do not ignore them! if ((i == cword)); then - local subcommandMatches - subcommandMatches=$(grep -c "^$subcommand" <<< "$_hledger_complist_commands") - if ((subcommandMatches > 1)); then - subcommand= - break - else - _hledger_compreply "$subcommand" - return 0 - fi + compopt +o filenames + _hledger_compreply "$(_hledger_compgen "$_hledger_complist_commands")" + return 0 fi break done From b6a4a887b9acff8feb8ad9e3a66868fa1b173094 Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Sun, 13 Dec 2020 07:56:56 +0100 Subject: [PATCH 38/82] White space --- shell-completion/hledger-completion.bash | 4 ++-- shell-completion/hledger-completion.bash.m4 | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index 0b8ecd48c..8ebb3424c 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -190,7 +190,7 @@ _hledger_compgen() { quoted[i++]=$word done <<< "$complist" - if (( $# < 3 )); then + if (($# < 3)); then match=${cur:${#prefix}} fi @@ -317,7 +317,7 @@ _hledger_optarg() { # hledger balance --file ~/ledger _ # 0 1 2 3 4 - for (( i=1; i < ${#words[@]} - 2; i++ )); do + for ((i=1; i < ${#words[@]} - 2; i++)); do offset=0 for j in "${!options[@]}"; do if [[ ${words[i]} == "${options[j]}" ]]; then diff --git a/shell-completion/hledger-completion.bash.m4 b/shell-completion/hledger-completion.bash.m4 index 9bfc23a32..26f58a594 100644 --- a/shell-completion/hledger-completion.bash.m4 +++ b/shell-completion/hledger-completion.bash.m4 @@ -190,7 +190,7 @@ _hledger_compgen() { quoted[i++]=$word done <<< "$complist" - if (( $# < 3 )); then + if (($# < 3)); then match=${cur:${#prefix}} fi @@ -317,7 +317,7 @@ _hledger_optarg() { # hledger balance --file ~/ledger _ # 0 1 2 3 4 - for (( i=1; i < ${#words[@]} - 2; i++ )); do + for ((i=1; i < ${#words[@]} - 2; i++)); do offset=0 for j in "${!options[@]}"; do if [[ ${words[i]} == "${options[j]}" ]]; then From eea7f0d5065ae8697406514ee29cd51c0bfe13a0 Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Sun, 13 Dec 2020 08:24:16 +0100 Subject: [PATCH 39/82] Add an early return from option argument completion Do not even enter the case statement if preceding words don't look like an option --- shell-completion/hledger-completion.bash | 2 ++ shell-completion/hledger-completion.bash.m4 | 2 ++ 2 files changed, 4 insertions(+) diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index 8ebb3424c..7c6fb4f9e 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -213,6 +213,8 @@ _hledger_compreply_optarg() { optionIndex=$((cword - 2)) fi + [[ ${words[optionIndex]} == -* ]] || return + case ${words[optionIndex]} in --alias) compopt -o nospace diff --git a/shell-completion/hledger-completion.bash.m4 b/shell-completion/hledger-completion.bash.m4 index 26f58a594..7aeb4593c 100644 --- a/shell-completion/hledger-completion.bash.m4 +++ b/shell-completion/hledger-completion.bash.m4 @@ -213,6 +213,8 @@ _hledger_compreply_optarg() { optionIndex=$((cword - 2)) fi + [[ ${words[optionIndex]} == -* ]] || return + case ${words[optionIndex]} in --alias) compopt -o nospace From 3cabee2470f99eae2e71ea35adcb73c1a38bcbae Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Sun, 13 Dec 2020 08:39:56 +0100 Subject: [PATCH 40/82] Remove unnecessary quoting, a matter of consistency... --- shell-completion/hledger-completion.bash | 4 ++-- shell-completion/hledger-completion.bash.m4 | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index 7c6fb4f9e..d10e717b2 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -17,9 +17,9 @@ _hledger_completion_function() { # - option processing assumes that `=` is in COMP_WORDBREAKS # - use compopt -o filenames to escape the rest COMP_WORDBREAKS=${COMP_WORDBREAKS//:} - case "$COMP_WORDBREAKS" in + case $COMP_WORDBREAKS in *=*) : ;; - *) COMP_WORDBREAKS="$COMP_WORDBREAKS=" ;; + *) COMP_WORDBREAKS=$COMP_WORDBREAKS= ;; esac compopt -o filenames diff --git a/shell-completion/hledger-completion.bash.m4 b/shell-completion/hledger-completion.bash.m4 index 7aeb4593c..a36119a59 100644 --- a/shell-completion/hledger-completion.bash.m4 +++ b/shell-completion/hledger-completion.bash.m4 @@ -17,9 +17,9 @@ _hledger_completion_function() { # - option processing assumes that `=` is in COMP_WORDBREAKS # - use compopt -o filenames to escape the rest COMP_WORDBREAKS=${COMP_WORDBREAKS//:} - case "$COMP_WORDBREAKS" in + case $COMP_WORDBREAKS in *=*) : ;; - *) COMP_WORDBREAKS="$COMP_WORDBREAKS=" ;; + *) COMP_WORDBREAKS=$COMP_WORDBREAKS= ;; esac compopt -o filenames From 83cfd104f979039c04e4579a0b2a80dd5af35b21 Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Sun, 13 Dec 2020 09:21:31 +0100 Subject: [PATCH 41/82] Remove _hledger_quote(), unused. --- shell-completion/hledger-completion.bash | 7 ------- shell-completion/hledger-completion.bash.m4 | 7 ------- 2 files changed, 14 deletions(-) diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index d10e717b2..201c03354 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -143,13 +143,6 @@ _hledger_quote_by_ref() [[ ${!2} == \$* ]] && eval "$2=${!2}" } -_hledger_quote() -{ - local quoted - _hledger_quote_by_ref "$1" quoted - printf %s "$quoted" -} - # Set the value of COMPREPLY from newline delimited completion candidates _hledger_compreply() { local IFS=$'\n' diff --git a/shell-completion/hledger-completion.bash.m4 b/shell-completion/hledger-completion.bash.m4 index a36119a59..7bce3c816 100644 --- a/shell-completion/hledger-completion.bash.m4 +++ b/shell-completion/hledger-completion.bash.m4 @@ -143,13 +143,6 @@ _hledger_quote_by_ref() [[ ${!2} == \$* ]] && eval "$2=${!2}" } -_hledger_quote() -{ - local quoted - _hledger_quote_by_ref "$1" quoted - printf %s "$quoted" -} - # Set the value of COMPREPLY from newline delimited completion candidates _hledger_compreply() { local IFS=$'\n' From 750450915d0eb776ef27ba7b1647ee669d2f9bf5 Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Sun, 13 Dec 2020 09:44:13 +0100 Subject: [PATCH 42/82] Break overly long lines for better readability Hopefully differences are aligned and easily discernible this way --- shell-completion/hledger-completion.bash | 44 +++++++++++++++------ shell-completion/hledger-completion.bash.m4 | 44 +++++++++++++++------ 2 files changed, 66 insertions(+), 22 deletions(-) diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index 201c03354..f7b17f6b2 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -37,7 +37,9 @@ _hledger_completion_function() { # Do not ignore them! if ((i == cword)); then compopt +o filenames - _hledger_compreply "$(_hledger_compgen "$_hledger_complist_commands")" + _hledger_compreply "$( + _hledger_compgen "$_hledger_complist_commands" + )" return 0 fi break @@ -49,12 +51,16 @@ _hledger_completion_function() { if [[ -z $subcommand ]]; then compopt +o filenames if [[ $cur == -* ]]; then - _hledger_compreply "$(_hledger_compgen "$_hledger_complist_generic_options")" + _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")" + _hledger_compreply "$( + _hledger_compgen "$_hledger_complist_commands" + )" fi return 0 @@ -80,7 +86,9 @@ _hledger_completion_function() { case $subcommand in help) compopt -o nosort +o filenames - _hledger_compreply "$(compgen -W "$(hledger help | tail -n 1)" -- "$cur")" + _hledger_compreply "$( + compgen -W "$(hledger help | tail -n 1)" -- "$cur" + )" return 0 ;; # These do not expect or support any query arguments @@ -94,9 +102,13 @@ _hledger_completion_function() { compopt -o nosort -o nospace _hledger_compreply "$(_hledger_compgen "$_hledger_complist_query_filters")" if [[ -z $cur ]]; then - _hledger_compreply_append "$(_hledger_compgen "$(_hledger accounts --flat --depth 1)")" + _hledger_compreply_append "$( + _hledger_compgen "$(_hledger accounts --flat --depth 1)" + )" else - _hledger_compreply_append "$(_hledger_compgen "$(_hledger accounts --flat)")" + _hledger_compreply_append "$( + _hledger_compgen "$(_hledger accounts --flat)" + )" fi return 0 @@ -211,7 +223,9 @@ _hledger_compreply_optarg() { case ${words[optionIndex]} in --alias) compopt -o nospace - _hledger_compreply "$(_hledger_compgen "$(_hledger accounts --flat)" "" "$match")" + _hledger_compreply "$( + _hledger_compgen "$(_hledger accounts --flat)" "" "$match" + )" ;; -f|--file|--rules-file|-o|--output-file) _hledger_compreply "$(compgen -f -- "$match")" @@ -220,14 +234,18 @@ _hledger_compreply_optarg() { compopt -o nosort wordlist="code description note payee" _hledger_compreply "$(compgen -W "$wordlist" -- "$match")" - _hledger_compreply_append "$(_hledger_compgen "$(_hledger tags)" "" "$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")" + _hledger_compreply "$( + _hledger_compgen "$(_hledger commodities)" "" "$match" + )" ;; --color|--colour) compopt -o nosort @@ -240,7 +258,9 @@ _hledger_compreply_optarg() { ;; --close-acct|--open-acct) compopt -o nospace - _hledger_compreply "$(_hledger_compgen "$(_hledger accounts --flat)" "" "$match")" + _hledger_compreply "$( + _hledger_compgen "$(_hledger accounts --flat)" "" "$match" + )" ;; --debug) wordlist="{1..9}" @@ -289,7 +309,9 @@ _hledger_compreply_query() { *) return 1 ;; esac _get_comp_words_by_ref -n '<=>' -c cur - _hledger_compreply "$(compgen -P "$query" -W "$wordlist" -- "$match")" + _hledger_compreply "$( + compgen -P "$query" -W "$wordlist" -- "$match" + )" return 0 ;; esac diff --git a/shell-completion/hledger-completion.bash.m4 b/shell-completion/hledger-completion.bash.m4 index 7bce3c816..ac5ac6f3a 100644 --- a/shell-completion/hledger-completion.bash.m4 +++ b/shell-completion/hledger-completion.bash.m4 @@ -37,7 +37,9 @@ _hledger_completion_function() { # Do not ignore them! if ((i == cword)); then compopt +o filenames - _hledger_compreply "$(_hledger_compgen "$_hledger_complist_commands")" + _hledger_compreply "$( + _hledger_compgen "$_hledger_complist_commands" + )" return 0 fi break @@ -49,12 +51,16 @@ _hledger_completion_function() { if [[ -z $subcommand ]]; then compopt +o filenames if [[ $cur == -* ]]; then - _hledger_compreply "$(_hledger_compgen "$_hledger_complist_generic_options")" + _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")" + _hledger_compreply "$( + _hledger_compgen "$_hledger_complist_commands" + )" fi return 0 @@ -80,7 +86,9 @@ _hledger_completion_function() { case $subcommand in help) compopt -o nosort +o filenames - _hledger_compreply "$(compgen -W "$(hledger help | tail -n 1)" -- "$cur")" + _hledger_compreply "$( + compgen -W "$(hledger help | tail -n 1)" -- "$cur" + )" return 0 ;; # These do not expect or support any query arguments @@ -94,9 +102,13 @@ _hledger_completion_function() { compopt -o nosort -o nospace _hledger_compreply "$(_hledger_compgen "$_hledger_complist_query_filters")" if [[ -z $cur ]]; then - _hledger_compreply_append "$(_hledger_compgen "$(_hledger accounts --flat --depth 1)")" + _hledger_compreply_append "$( + _hledger_compgen "$(_hledger accounts --flat --depth 1)" + )" else - _hledger_compreply_append "$(_hledger_compgen "$(_hledger accounts --flat)")" + _hledger_compreply_append "$( + _hledger_compgen "$(_hledger accounts --flat)" + )" fi return 0 @@ -211,7 +223,9 @@ _hledger_compreply_optarg() { case ${words[optionIndex]} in --alias) compopt -o nospace - _hledger_compreply "$(_hledger_compgen "$(_hledger accounts --flat)" "" "$match")" + _hledger_compreply "$( + _hledger_compgen "$(_hledger accounts --flat)" "" "$match" + )" ;; -f|--file|--rules-file|-o|--output-file) _hledger_compreply "$(compgen -f -- "$match")" @@ -220,14 +234,18 @@ _hledger_compreply_optarg() { compopt -o nosort wordlist="code description note payee" _hledger_compreply "$(compgen -W "$wordlist" -- "$match")" - _hledger_compreply_append "$(_hledger_compgen "$(_hledger tags)" "" "$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")" + _hledger_compreply "$( + _hledger_compgen "$(_hledger commodities)" "" "$match" + )" ;; --color|--colour) compopt -o nosort @@ -240,7 +258,9 @@ _hledger_compreply_optarg() { ;; --close-acct|--open-acct) compopt -o nospace - _hledger_compreply "$(_hledger_compgen "$(_hledger accounts --flat)" "" "$match")" + _hledger_compreply "$( + _hledger_compgen "$(_hledger accounts --flat)" "" "$match" + )" ;; --debug) wordlist="{1..9}" @@ -289,7 +309,9 @@ _hledger_compreply_query() { *) return 1 ;; esac _get_comp_words_by_ref -n '<=>' -c cur - _hledger_compreply "$(compgen -P "$query" -W "$wordlist" -- "$match")" + _hledger_compreply "$( + compgen -P "$query" -W "$wordlist" -- "$match" + )" return 0 ;; esac From 05d483494b2fe5e418a5c7d2ebb5b71d2545ae2d Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Sun, 13 Dec 2020 09:56:04 +0100 Subject: [PATCH 43/82] Remove redundant call of _get_comp_words_by_ref() --- shell-completion/hledger-completion.bash | 1 - shell-completion/hledger-completion.bash.m4 | 1 - 2 files changed, 2 deletions(-) diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index f7b17f6b2..33d0c0249 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -308,7 +308,6 @@ _hledger_compreply_query() { status:) wordlist="\ * !" ;; *) return 1 ;; esac - _get_comp_words_by_ref -n '<=>' -c cur _hledger_compreply "$( compgen -P "$query" -W "$wordlist" -- "$match" )" diff --git a/shell-completion/hledger-completion.bash.m4 b/shell-completion/hledger-completion.bash.m4 index ac5ac6f3a..7fefb9330 100644 --- a/shell-completion/hledger-completion.bash.m4 +++ b/shell-completion/hledger-completion.bash.m4 @@ -308,7 +308,6 @@ _hledger_compreply_query() { status:) wordlist="\ * !" ;; *) return 1 ;; esac - _get_comp_words_by_ref -n '<=>' -c cur _hledger_compreply "$( compgen -P "$query" -W "$wordlist" -- "$match" )" From 1bbf04d310e5c14ea93fd066854dc031a369caa6 Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Sun, 13 Dec 2020 12:18:05 +0100 Subject: [PATCH 44/82] Edit comments --- shell-completion/hledger-completion.bash | 3 +-- shell-completion/hledger-completion.bash.m4 | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index 33d0c0249..701c568e3 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -54,7 +54,6 @@ _hledger_completion_function() { _hledger_compreply "$( _hledger_compgen "$_hledger_complist_generic_options" )" - # Suspend space on completion of long options requiring an argument [[ ${COMPREPLY[0]} == --*= ]] && compopt -o nospace else @@ -98,7 +97,7 @@ _hledger_completion_function() { esac # Offer query filters and accounts for the rest - # Do not sort. Keep options, accounts and query filters grouped separately + # Do not sort. Keep accounts and query filters grouped separately compopt -o nosort -o nospace _hledger_compreply "$(_hledger_compgen "$_hledger_complist_query_filters")" if [[ -z $cur ]]; then diff --git a/shell-completion/hledger-completion.bash.m4 b/shell-completion/hledger-completion.bash.m4 index 7fefb9330..1b6327f17 100644 --- a/shell-completion/hledger-completion.bash.m4 +++ b/shell-completion/hledger-completion.bash.m4 @@ -54,7 +54,6 @@ _hledger_completion_function() { _hledger_compreply "$( _hledger_compgen "$_hledger_complist_generic_options" )" - # Suspend space on completion of long options requiring an argument [[ ${COMPREPLY[0]} == --*= ]] && compopt -o nospace else @@ -98,7 +97,7 @@ _hledger_completion_function() { esac # Offer query filters and accounts for the rest - # Do not sort. Keep options, accounts and query filters grouped separately + # Do not sort. Keep accounts and query filters grouped separately compopt -o nosort -o nospace _hledger_compreply "$(_hledger_compgen "$_hledger_complist_query_filters")" if [[ -z $cur ]]; then From 78ce651531309b28da84485bb13a6e5a6070fffc Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Sun, 13 Dec 2020 14:47:19 +0100 Subject: [PATCH 45/82] White space, fix alignment --- shell-completion/hledger-completion.bash | 12 ++++++------ shell-completion/hledger-completion.bash.m4 | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index 701c568e3..fa9909956 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -287,12 +287,12 @@ _hledger_compreply_query() { local hledgerArgs=() case $query in acct:) - if (( ${#match} )); then - hledgerArgs=(accounts --flat) - else - hledgerArgs=(accounts --flat --depth 1) - fi - ;; + if (( ${#match} )); then + hledgerArgs=(accounts --flat) + else + hledgerArgs=(accounts --flat --depth 1) + fi + ;; code:) hledgerArgs=(codes) ;; cur:) hledgerArgs=(commodities) ;; desc:) hledgerArgs=(descriptions) ;; diff --git a/shell-completion/hledger-completion.bash.m4 b/shell-completion/hledger-completion.bash.m4 index 1b6327f17..22642c267 100644 --- a/shell-completion/hledger-completion.bash.m4 +++ b/shell-completion/hledger-completion.bash.m4 @@ -287,12 +287,12 @@ _hledger_compreply_query() { local hledgerArgs=() case $query in acct:) - if (( ${#match} )); then - hledgerArgs=(accounts --flat) - else - hledgerArgs=(accounts --flat --depth 1) - fi - ;; + if (( ${#match} )); then + hledgerArgs=(accounts --flat) + else + hledgerArgs=(accounts --flat --depth 1) + fi + ;; code:) hledgerArgs=(codes) ;; cur:) hledgerArgs=(commodities) ;; desc:) hledgerArgs=(descriptions) ;; From 797301c3dc09ec4b578500035bad58391b3e3e61 Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Sun, 13 Dec 2020 19:03:47 +0100 Subject: [PATCH 46/82] White space --- shell-completion/hledger-completion.bash | 4 +--- shell-completion/hledger-completion.bash.m4 | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index fa9909956..93333f75b 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -315,9 +315,7 @@ _hledger_compreply_query() { esac _hledger_compreply "$( - _hledger_compgen "$( - _hledger "${hledgerArgs[@]}" - )" "$query" + _hledger_compgen "$(_hledger "${hledgerArgs[@]}")" "$query" )" return 0 diff --git a/shell-completion/hledger-completion.bash.m4 b/shell-completion/hledger-completion.bash.m4 index 22642c267..f28f11048 100644 --- a/shell-completion/hledger-completion.bash.m4 +++ b/shell-completion/hledger-completion.bash.m4 @@ -315,9 +315,7 @@ _hledger_compreply_query() { esac _hledger_compreply "$( - _hledger_compgen "$( - _hledger "${hledgerArgs[@]}" - )" "$query" + _hledger_compgen "$(_hledger "${hledgerArgs[@]}")" "$query" )" return 0 From 34dbe3b3a8d090e1c2a50c6a10b7e6ecc6e62e7c Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Mon, 14 Dec 2020 04:50:28 +0100 Subject: [PATCH 47/82] Use compopt -o nospace only for a query prefix Similarly to long options treatment, suspend space only when completing what looks like a query or account prefix i.e. something ending with a colon (:). --- shell-completion/hledger-completion.bash | 7 +++++-- shell-completion/hledger-completion.bash.m4 | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index 93333f75b..274d85808 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -97,8 +97,6 @@ _hledger_completion_function() { esac # Offer query filters and accounts for the rest - # Do not sort. Keep accounts and query filters grouped separately - compopt -o nosort -o nospace _hledger_compreply "$(_hledger_compgen "$_hledger_complist_query_filters")" if [[ -z $cur ]]; then _hledger_compreply_append "$( @@ -110,6 +108,11 @@ _hledger_completion_function() { )" 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 } diff --git a/shell-completion/hledger-completion.bash.m4 b/shell-completion/hledger-completion.bash.m4 index f28f11048..9d0d8538f 100644 --- a/shell-completion/hledger-completion.bash.m4 +++ b/shell-completion/hledger-completion.bash.m4 @@ -97,8 +97,6 @@ _hledger_completion_function() { esac # Offer query filters and accounts for the rest - # Do not sort. Keep accounts and query filters grouped separately - compopt -o nosort -o nospace _hledger_compreply "$(_hledger_compgen "$_hledger_complist_query_filters")" if [[ -z $cur ]]; then _hledger_compreply_append "$( @@ -110,6 +108,11 @@ _hledger_completion_function() { )" 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 } From 0f8c01f0338e4aaed7efdd017b60e39d10d77504 Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Mon, 14 Dec 2020 06:23:25 +0100 Subject: [PATCH 48/82] Delay setting `compopt -o filenames` ...until just before query completion. It is not desired for commands and options completion and is better used selectively only when really needed. --- shell-completion/hledger-completion.bash | 15 ++++++++------- shell-completion/hledger-completion.bash.m4 | 15 ++++++++------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index 274d85808..48472aa83 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -15,13 +15,12 @@ _hledger_completion_function() { # Current treatment for special characters: # - exclude colon (:) from COMP_WORDBREAKS # - option processing assumes that `=` is in COMP_WORDBREAKS - # - use compopt -o filenames to escape the rest + # - use compopt -o filenames selectively to escape the rest COMP_WORDBREAKS=${COMP_WORDBREAKS//:} case $COMP_WORDBREAKS in *=*) : ;; *) COMP_WORDBREAKS=$COMP_WORDBREAKS= ;; esac - compopt -o filenames local subcommand local i @@ -36,7 +35,6 @@ _hledger_completion_function() { # $subcommand == bal --> balance, balancesheet, balancesheetequity, etc. # Do not ignore them! if ((i == cword)); then - compopt +o filenames _hledger_compreply "$( _hledger_compgen "$_hledger_complist_commands" )" @@ -49,7 +47,6 @@ _hledger_completion_function() { _hledger_compreply_optarg && return if [[ -z $subcommand ]]; then - compopt +o filenames if [[ $cur == -* ]]; then _hledger_compreply "$( _hledger_compgen "$_hledger_complist_generic_options" @@ -73,11 +70,14 @@ _hledger_completion_function() { # Suspend space on completion of long options requiring an argument [[ ${COMPREPLY[0]} == --*= ]] && compopt -o nospace - compopt +o filenames 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 @@ -224,12 +224,13 @@ _hledger_compreply_optarg() { case ${words[optionIndex]} in --alias) - compopt -o nospace + 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) @@ -259,7 +260,7 @@ _hledger_compreply_optarg() { _hledger_compreply "$(compgen -W "$wordlist" -- "$match")" ;; --close-acct|--open-acct) - compopt -o nospace + compopt -o nospace -o filenames _hledger_compreply "$( _hledger_compgen "$(_hledger accounts --flat)" "" "$match" )" diff --git a/shell-completion/hledger-completion.bash.m4 b/shell-completion/hledger-completion.bash.m4 index 9d0d8538f..34507a1cf 100644 --- a/shell-completion/hledger-completion.bash.m4 +++ b/shell-completion/hledger-completion.bash.m4 @@ -15,13 +15,12 @@ _hledger_completion_function() { # Current treatment for special characters: # - exclude colon (:) from COMP_WORDBREAKS # - option processing assumes that `=` is in COMP_WORDBREAKS - # - use compopt -o filenames to escape the rest + # - use compopt -o filenames selectively to escape the rest COMP_WORDBREAKS=${COMP_WORDBREAKS//:} case $COMP_WORDBREAKS in *=*) : ;; *) COMP_WORDBREAKS=$COMP_WORDBREAKS= ;; esac - compopt -o filenames local subcommand local i @@ -36,7 +35,6 @@ _hledger_completion_function() { # $subcommand == bal --> balance, balancesheet, balancesheetequity, etc. # Do not ignore them! if ((i == cword)); then - compopt +o filenames _hledger_compreply "$( _hledger_compgen "$_hledger_complist_commands" )" @@ -49,7 +47,6 @@ _hledger_completion_function() { _hledger_compreply_optarg && return if [[ -z $subcommand ]]; then - compopt +o filenames if [[ $cur == -* ]]; then _hledger_compreply "$( _hledger_compgen "$_hledger_complist_generic_options" @@ -73,11 +70,14 @@ _hledger_completion_function() { # Suspend space on completion of long options requiring an argument [[ ${COMPREPLY[0]} == --*= ]] && compopt -o nospace - compopt +o filenames 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 @@ -224,12 +224,13 @@ _hledger_compreply_optarg() { case ${words[optionIndex]} in --alias) - compopt -o nospace + 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) @@ -259,7 +260,7 @@ _hledger_compreply_optarg() { _hledger_compreply "$(compgen -W "$wordlist" -- "$match")" ;; --close-acct|--open-acct) - compopt -o nospace + compopt -o nospace -o filenames _hledger_compreply "$( _hledger_compgen "$(_hledger accounts --flat)" "" "$match" )" From 3eccfb85e04e8de9f7a5a9cbc9c9be47ce420e9f Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Mon, 14 Dec 2020 06:35:15 +0100 Subject: [PATCH 49/82] Remove `compopt -o nospace` from --{close,open}-acct handler --- shell-completion/hledger-completion.bash | 2 +- shell-completion/hledger-completion.bash.m4 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index 48472aa83..3310c4f52 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -260,7 +260,7 @@ _hledger_compreply_optarg() { _hledger_compreply "$(compgen -W "$wordlist" -- "$match")" ;; --close-acct|--open-acct) - compopt -o nospace -o filenames + compopt -o filenames _hledger_compreply "$( _hledger_compgen "$(_hledger accounts --flat)" "" "$match" )" diff --git a/shell-completion/hledger-completion.bash.m4 b/shell-completion/hledger-completion.bash.m4 index 34507a1cf..b63ba91a0 100644 --- a/shell-completion/hledger-completion.bash.m4 +++ b/shell-completion/hledger-completion.bash.m4 @@ -260,7 +260,7 @@ _hledger_compreply_optarg() { _hledger_compreply "$(compgen -W "$wordlist" -- "$match")" ;; --close-acct|--open-acct) - compopt -o nospace -o filenames + compopt -o filenames _hledger_compreply "$( _hledger_compgen "$(_hledger accounts --flat)" "" "$match" )" From d82e13bb5119fadbeeb7a90d6ece2384abd23db1 Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Mon, 14 Dec 2020 07:37:48 +0100 Subject: [PATCH 50/82] Add an automatic check for required option argument Get rid of manually listing unhandled long options: Instead of listing options requiring an argument one by one in the case statement in _hledger_compreply_optarg(), the option completion lists are searched and if the option does require an argument an empty COMPREPLY is send. Short options are uncounted for and still need a manual entry. This necessitated setting $subcommandOptions beforehand, so it is moved back where it was -- in the sub-command loop in main(). --- shell-completion/hledger-completion.bash | 49 ++++++++++++++------- shell-completion/hledger-completion.bash.m4 | 49 ++++++++++++++------- 2 files changed, 64 insertions(+), 34 deletions(-) diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index 3310c4f52..00aa0222e 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -23,6 +23,7 @@ _hledger_completion_function() { esac local subcommand + local subcommandOptions local i for ((i=1; i<${#words[@]}; i++)); do subcommand=${words[i]} @@ -40,6 +41,16 @@ _hledger_completion_function() { )" return 0 fi + if [[ $cur == -* ]]; then + # Replace dashes with underscores and use indirect expansion + subcommandOptions=_hledger_complist_options_${subcommand//-/_} + _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 @@ -62,18 +73,6 @@ _hledger_completion_function() { return 0 fi - if [[ $cur == -* ]]; then - local subcommandOptions - # Replace dashes with underscores and use indirect expansion - subcommandOptions=_hledger_complist_options_${subcommand//-/_} - _hledger_compreply "$(_hledger_compgen "${!subcommandOptions}")" - - # Suspend space on completion of long options requiring an argument - [[ ${COMPREPLY[0]} == --*= ]] && compopt -o nospace - - 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 @@ -208,7 +207,7 @@ _hledger_compgen() { # 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 optionIndex=$((cword - 1)) + local option=${words[cword - 1]} local match=$cur local wordlist @@ -217,12 +216,12 @@ _hledger_compreply_optarg() { match="" # Once input is present, cword is incremented so we compensate elif [[ $prev == = ]]; then - optionIndex=$((cword - 2)) + option=${words[cword - 2]} fi - [[ ${words[optionIndex]} == -* ]] || return + [[ $option == -* ]] || return - case ${words[optionIndex]} in + case $option in --alias) compopt -o nospace -o filenames _hledger_compreply "$( @@ -270,10 +269,26 @@ _hledger_compreply_optarg() { _hledger_compreply "$(compgen -W "$wordlist" -- "$match")" ;; # Argument required, but no handler (yet) - -b|--begin|-e|--end|-p|--period|--depth|--drop) + -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 diff --git a/shell-completion/hledger-completion.bash.m4 b/shell-completion/hledger-completion.bash.m4 index b63ba91a0..cf6749c6d 100644 --- a/shell-completion/hledger-completion.bash.m4 +++ b/shell-completion/hledger-completion.bash.m4 @@ -23,6 +23,7 @@ _hledger_completion_function() { esac local subcommand + local subcommandOptions local i for ((i=1; i<${#words[@]}; i++)); do subcommand=${words[i]} @@ -40,6 +41,16 @@ _hledger_completion_function() { )" return 0 fi + if [[ $cur == -* ]]; then + # Replace dashes with underscores and use indirect expansion + subcommandOptions=_hledger_complist_options_${subcommand//-/_} + _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 @@ -62,18 +73,6 @@ _hledger_completion_function() { return 0 fi - if [[ $cur == -* ]]; then - local subcommandOptions - # Replace dashes with underscores and use indirect expansion - subcommandOptions=_hledger_complist_options_${subcommand//-/_} - _hledger_compreply "$(_hledger_compgen "${!subcommandOptions}")" - - # Suspend space on completion of long options requiring an argument - [[ ${COMPREPLY[0]} == --*= ]] && compopt -o nospace - - 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 @@ -208,7 +207,7 @@ _hledger_compgen() { # 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 optionIndex=$((cword - 1)) + local option=${words[cword - 1]} local match=$cur local wordlist @@ -217,12 +216,12 @@ _hledger_compreply_optarg() { match="" # Once input is present, cword is incremented so we compensate elif [[ $prev == = ]]; then - optionIndex=$((cword - 2)) + option=${words[cword - 2]} fi - [[ ${words[optionIndex]} == -* ]] || return + [[ $option == -* ]] || return - case ${words[optionIndex]} in + case $option in --alias) compopt -o nospace -o filenames _hledger_compreply "$( @@ -270,10 +269,26 @@ _hledger_compreply_optarg() { _hledger_compreply "$(compgen -W "$wordlist" -- "$match")" ;; # Argument required, but no handler (yet) - -b|--begin|-e|--end|-p|--period|--depth|--drop) + -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 From 910e86b4b2baee430359d6da3b39cbd98d2f21fd Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Mon, 14 Dec 2020 08:10:16 +0100 Subject: [PATCH 51/82] Fix a7dc62d: set $subcommandOptions unconditionally --- shell-completion/hledger-completion.bash | 9 +++++---- shell-completion/hledger-completion.bash.m4 | 9 +++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index 00aa0222e..d6dc14502 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -41,11 +41,12 @@ _hledger_completion_function() { )" return 0 fi - if [[ $cur == -* ]]; then - # Replace dashes with underscores and use indirect expansion - subcommandOptions=_hledger_complist_options_${subcommand//-/_} - _hledger_compreply "$(_hledger_compgen "${!subcommandOptions}")" + # 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 diff --git a/shell-completion/hledger-completion.bash.m4 b/shell-completion/hledger-completion.bash.m4 index cf6749c6d..bb1499867 100644 --- a/shell-completion/hledger-completion.bash.m4 +++ b/shell-completion/hledger-completion.bash.m4 @@ -41,11 +41,12 @@ _hledger_completion_function() { )" return 0 fi - if [[ $cur == -* ]]; then - # Replace dashes with underscores and use indirect expansion - subcommandOptions=_hledger_complist_options_${subcommand//-/_} - _hledger_compreply "$(_hledger_compgen "${!subcommandOptions}")" + # 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 From fb3577d3e0c98b8461149f6f9eb4a1a56d370c3d Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Mon, 14 Dec 2020 19:39:33 +0100 Subject: [PATCH 52/82] return 0 Shame on me... --- shell-completion/hledger-completion.bash | 2 ++ shell-completion/hledger-completion.bash.m4 | 2 ++ 2 files changed, 4 insertions(+) diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index d6dc14502..3f34bd8ba 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -2033,6 +2033,8 @@ TEXT read -r -d "" _hledger_complist_options_api < Date: Tue, 15 Dec 2020 08:38:21 +0100 Subject: [PATCH 53/82] Fix _hledger_extension_completion when called by path... Could break if called with ./funky-path/with-dashes/hledger-ui --- shell-completion/hledger-completion.bash | 8 +++++--- shell-completion/hledger-completion.bash.m4 | 8 +++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index 3f34bd8ba..c61cb9e42 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -117,9 +117,11 @@ _hledger_completion_function() { } _hledger_extension_completion_function() { - # Pretend that hledger is called with given extension - # as first argument and call main completion function - COMP_WORDS=("hledger" "${1#*-}" "${COMP_WORDS[@]:1}") + 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_function "hledger" "${@:1}" } diff --git a/shell-completion/hledger-completion.bash.m4 b/shell-completion/hledger-completion.bash.m4 index 9c7785848..296dcf5b1 100644 --- a/shell-completion/hledger-completion.bash.m4 +++ b/shell-completion/hledger-completion.bash.m4 @@ -117,9 +117,11 @@ _hledger_completion_function() { } _hledger_extension_completion_function() { - # Pretend that hledger is called with given extension - # as first argument and call main completion function - COMP_WORDS=("hledger" "${1#*-}" "${COMP_WORDS[@]:1}") + 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_function "hledger" "${@:1}" } From 99af527f166f58b28fc4572138b556d388abee84 Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Tue, 15 Dec 2020 08:45:44 +0100 Subject: [PATCH 54/82] Remove _function from function names --- shell-completion/hledger-completion.bash | 11 +++++------ shell-completion/hledger-completion.bash.m4 | 11 +++++------ 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index c61cb9e42..43497bfe1 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -8,7 +8,7 @@ # That said, we *do* remove colon (:) from COMP_WORDBREAKS which impacts # the rest of the session and completion for other programs. -_hledger_completion_function() { +_hledger_completion() { local cur prev words cword _init_completion -n : || return 0 @@ -116,22 +116,21 @@ _hledger_completion_function() { return 0 } -_hledger_extension_completion_function() { +_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_function "hledger" "${@: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 diff --git a/shell-completion/hledger-completion.bash.m4 b/shell-completion/hledger-completion.bash.m4 index 296dcf5b1..e6eaef966 100644 --- a/shell-completion/hledger-completion.bash.m4 +++ b/shell-completion/hledger-completion.bash.m4 @@ -8,7 +8,7 @@ # That said, we *do* remove colon (:) from COMP_WORDBREAKS which impacts # the rest of the session and completion for other programs. -_hledger_completion_function() { +_hledger_completion() { local cur prev words cword _init_completion -n : || return 0 @@ -116,22 +116,21 @@ _hledger_completion_function() { return 0 } -_hledger_extension_completion_function() { +_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_function "hledger" "${@: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 From bee666704b0c39f5f0c18340c6f58793d1ff4908 Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Tue, 15 Dec 2020 09:43:06 +0100 Subject: [PATCH 55/82] Add a comment with example installation instructions --- shell-completion/hledger-completion.bash | 17 +++++++++++++++++ shell-completion/hledger-completion.bash.m4 | 17 +++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index 43497bfe1..1ee302c4c 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -8,6 +8,23 @@ # 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 diff --git a/shell-completion/hledger-completion.bash.m4 b/shell-completion/hledger-completion.bash.m4 index e6eaef966..78bb79abc 100644 --- a/shell-completion/hledger-completion.bash.m4 +++ b/shell-completion/hledger-completion.bash.m4 @@ -8,6 +8,23 @@ # 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 From 13bf6fdcd8bc55112a89a3f2102a8c6f40bc0034 Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Tue, 15 Dec 2020 09:48:14 +0100 Subject: [PATCH 56/82] Amend installation comment --- shell-completion/hledger-completion.bash | 4 ++-- shell-completion/hledger-completion.bash.m4 | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index 1ee302c4c..2d86c2d33 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -10,13 +10,13 @@ # 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 && diff --git a/shell-completion/hledger-completion.bash.m4 b/shell-completion/hledger-completion.bash.m4 index 78bb79abc..1c637f188 100644 --- a/shell-completion/hledger-completion.bash.m4 +++ b/shell-completion/hledger-completion.bash.m4 @@ -10,13 +10,13 @@ # 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 && From bc66b235202865460a7745853578358e0337ed5d Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Wed, 16 Dec 2020 06:24:53 +0100 Subject: [PATCH 57/82] Fix build race condition Make `command-options` a prerequisite of `hledger-completion.bash`. Currently the build succeeds only because the former takes less time to finish than all the prerequisites of the latter. If you run a `make clean && make -j 4`, the build would fail as they are built in parallel. --- shell-completion/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shell-completion/Makefile b/shell-completion/Makefile index a838b5dc4..10f64b171 100644 --- a/shell-completion/Makefile +++ b/shell-completion/Makefile @@ -1,9 +1,9 @@ .PHONY: command-options clean -all: command-options hledger-completion.bash +all: hledger-completion.bash -hledger-completion.bash: hledger-completion.bash.m4 commands-list.txt query-filters.txt generic-options.txt +hledger-completion.bash: hledger-completion.bash.m4 commands-list.txt query-filters.txt generic-options.txt command-options m4 hledger-completion.bash.m4 > $@ generic-options.txt: From bb8118c77101988627a8a840484bb322bb2bdbc1 Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Thu, 17 Dec 2020 10:05:10 +0100 Subject: [PATCH 58/82] Isolate shell code in a stub file included by m4 This way we can easily edit m4 in m4-mode and the shell script stub in sh-mode and prevent subtle errors coming from accidental quoting issues or macros (mis)interpreted by m4. --- shell-completion/hledger-completion.bash | 18 +- shell-completion/hledger-completion.bash.m4 | 415 +----------------- shell-completion/hledger-completion.bash.stub | 410 +++++++++++++++++ 3 files changed, 418 insertions(+), 425 deletions(-) create mode 100644 shell-completion/hledger-completion.bash.stub diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index 2d86c2d33..8a6db7e7c 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -1,4 +1,6 @@ -# shellcheck disable=2034 +# -*- mode: sh; sh-basic-offset: 4; indent-tabs-mode: nil -*- +# ex: ts=4 sw=4 et +# shellcheck disable=2034,2154 # Completion script for hledger. # Created using a Makefile and real hledger. @@ -415,6 +417,7 @@ read -r -d "" _hledger_complist_commands < 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 - help) - compopt -o nosort +o filenames - _hledger_compreply "$( - compgen -W "$(hledger help | tail -n 1)" -- "$cur" - )" - return 0 - ;; - # These do not expect or support any query arguments - commodities|check-dupes|files|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 -} +include(`hledger-completion.bash.stub')dnl # Include lists of commands and options generated by the Makefile using the # m4 macro processor. @@ -434,9 +27,3 @@ TEXT ')dnl return 0 - -# Local Variables: -# sh-basic-offset: 4 -# indent-tabs-mode: nil -# End: -# ex: ts=4 sw=4 et diff --git a/shell-completion/hledger-completion.bash.stub b/shell-completion/hledger-completion.bash.stub new file mode 100644 index 000000000..0853dbdd8 --- /dev/null +++ b/shell-completion/hledger-completion.bash.stub @@ -0,0 +1,410 @@ +# -*- mode: sh; sh-basic-offset: 4; indent-tabs-mode: nil -*- +# ex: 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 + help) + compopt -o nosort +o filenames + _hledger_compreply "$( + compgen -W "$(hledger help | tail -n 1)" -- "$cur" + )" + return 0 + ;; + # These do not expect or support any query arguments + commodities|check-dupes|files|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 +} From 4a32ebf8b5a43c8b020dcdfdddd3d63ad6ccb823 Mon Sep 17 00:00:00 2001 From: Caleb Maclennan Date: Wed, 16 Dec 2020 11:19:06 +0300 Subject: [PATCH 59/82] =?UTF-8?q?Eliminate=20dependency=20on=20=E2=80=98pa?= =?UTF-8?q?ste=E2=80=99=20and=20=E2=80=98parallel=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shell-completion/Makefile | 26 +++++++++++++++----------- shell-completion/README.md | 6 ++++-- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/shell-completion/Makefile b/shell-completion/Makefile index 10f64b171..abdf75366 100644 --- a/shell-completion/Makefile +++ b/shell-completion/Makefile @@ -1,30 +1,34 @@ - -.PHONY: command-options clean +# 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) all: hledger-completion.bash -hledger-completion.bash: hledger-completion.bash.m4 commands-list.txt query-filters.txt generic-options.txt command-options +COMMANDS := $(sort $(shell hledger | ./output-commands.sh | grep -v ^hledger | sort -u) ui web api) +CMDOPTFILES := $(foreach CMD,$(COMMANDS),options-$(CMD).txt) + +hledger-completion.bash: hledger-completion.bash.m4 commands.txt commands-list.txt query-filters.txt generic-options.txt $(CMDOPTFILES) m4 hledger-completion.bash.m4 > $@ generic-options.txt: hledger -h | ./output-options.sh | sort -u > $@ commands.txt: - hledger | ./output-commands.sh | grep -v ^hledger | sort -u > $@ - 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' -command-options: commands.txt - parallel -j8 'hledger {} -h | ./output-options.sh {} | sort -u > options-{}.txt' < commands.txt +options-%.txt: + hledger $* -h | ./output-options.sh $* | sort -u > $@ +.PHONY: clean clean: rm -f commands*.txt generic-options.txt options-*.txt rm -f hledger-completion.bash diff --git a/shell-completion/README.md b/shell-completion/README.md index 8c9d961ab..9804b608c 100644 --- a/shell-completion/README.md +++ b/shell-completion/README.md @@ -69,8 +69,10 @@ 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. From 0498b8ff7c0401d23accb8634cf293632ad95d26 Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Fri, 18 Dec 2020 06:04:14 +0100 Subject: [PATCH 60/82] Add stub file to m4 build target prerequisites And as it becomes unwieldy, put all the dependencies in a variable --- shell-completion/Makefile | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/shell-completion/Makefile b/shell-completion/Makefile index abdf75366..b983ec2f4 100644 --- a/shell-completion/Makefile +++ b/shell-completion/Makefile @@ -9,7 +9,17 @@ all: hledger-completion.bash COMMANDS := $(sort $(shell hledger | ./output-commands.sh | grep -v ^hledger | sort -u) ui web api) CMDOPTFILES := $(foreach CMD,$(COMMANDS),options-$(CMD).txt) -hledger-completion.bash: hledger-completion.bash.m4 commands.txt commands-list.txt query-filters.txt generic-options.txt $(CMDOPTFILES) +define M4DEPS := +hledger-completion.bash.m4 \ +hledger-completion.bash.stub \ +commands.txt \ +commands-list.txt \ +query-filters.txt \ +generic-options.txt \ +$(CMDOPTFILES) +endef + +hledger-completion.bash: $(M4DEPS) m4 hledger-completion.bash.m4 > $@ generic-options.txt: From 1b6f968a6a3c2dc488702b174a46b473fc5bd111 Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Fri, 18 Dec 2020 06:24:30 +0100 Subject: [PATCH 61/82] Move commands and options parsing into the Makefile It is just a pipe of sed regex filters and all can be viewed/edited in one place. Add a couple of debug targets to see easily the effects of regex changes. --- shell-completion/Makefile | 53 +++++++++++++++++++++++------ shell-completion/output-commands.sh | 19 ----------- shell-completion/output-options.sh | 24 ------------- 3 files changed, 42 insertions(+), 54 deletions(-) delete mode 100755 shell-completion/output-commands.sh delete mode 100755 shell-completion/output-options.sh diff --git a/shell-completion/Makefile b/shell-completion/Makefile index b983ec2f4..d821ec213 100644 --- a/shell-completion/Makefile +++ b/shell-completion/Makefile @@ -4,9 +4,32 @@ # get parallel jobs. MAKEFLAGS += --jobs=$(shell nproc 2>/dev/null || printf 8) -all: hledger-completion.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. +COMMANDS_TMP := commands.tmp -COMMANDS := $(sort $(shell hledger | ./output-commands.sh | grep -v ^hledger | sort -u) ui web api) +define PARSE_COMMANDS := +hledger > $(COMMANDS_TMP) ; +{ \ + sed -rn 's/^\s+([a-z][-a-z]+)\s+.*/\1/p' $(COMMANDS_TMP) ; \ + sed -rn 's/^\s+[a-z][-a-z]+\s+\(([a-z][ ,a-z]+)\).*/\1/p' $(COMMANDS_TMP) | \ + sed 's/\s*,\s*/\n/g' | \ + sed '/^.$$/d' ; \ +} | sed '/^hledger/d' | sort -u +endef + +# 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 (=) +define PARSE_OPTIONS := +sed -rn '/^\s+-/p' | \ +sed -rn 's/^\s{1,4}(-.)?\s{1,4}(--[a-zA-Z][-_a-zA-Z0-9]+=?).*/\2/p' | \ +sort -u +endef + +EXTENSIONS := ui web api + +COMMANDS := $(sort $(shell $(PARSE_COMMANDS)) $(EXTENSIONS)) CMDOPTFILES := $(foreach CMD,$(COMMANDS),options-$(CMD).txt) define M4DEPS := @@ -19,27 +42,35 @@ generic-options.txt \ $(CMDOPTFILES) endef +all: hledger-completion.bash + hledger-completion.bash: $(M4DEPS) m4 hledger-completion.bash.m4 > $@ -generic-options.txt: - hledger -h | ./output-options.sh | sort -u > $@ - commands.txt: printf "%s\n" $(COMMANDS) > $@ 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: + hledger -h | $(PARSE_OPTIONS) > $@ options-%.txt: - hledger $* -h | ./output-options.sh $* | sort -u > $@ + hledger $* -h | $(PARSE_OPTIONS) > $@ .PHONY: clean clean: - rm -f commands*.txt generic-options.txt options-*.txt + rm -f commands*.{txt,tmp} generic-options.txt options-*.txt rm -f hledger-completion.bash - rm -rf _{commands,options}.tmp + + +# Basic REGEX debugging targets +.PHONY: debug-commands debug-options + +debug-commands: + $(PARSE_COMMANDS) + +debug-options: CMD := +debug-options: + hledger $(CMD) -h | $(PARSE_OPTIONS) diff --git a/shell-completion/output-commands.sh b/shell-completion/output-commands.sh deleted file mode 100755 index ae1b67987..000000000 --- a/shell-completion/output-commands.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash -# Output subcommands from man/usage text - -set -o errexit -o pipefail -o nounset - -main() { - declare tmp="_commands.tmp" - cat > "$tmp" - - sed -rn 's/^\s+([a-z][-a-z]+)\s+.*/\1/p' "$tmp" - - # Output command aliases in parenthesis: - # Do not output single letter command aliases, it's not useful. - sed -rn 's/^\s+[a-z][-a-z]+\s+\(([a-z][ ,a-z]+)\).*/\1/p' "$tmp" | - sed 's/\s*,\s*/\n/g' | - sed '/^.$/d' -} - -main "$@" diff --git a/shell-completion/output-options.sh b/shell-completion/output-options.sh deleted file mode 100755 index fbc9e5471..000000000 --- a/shell-completion/output-options.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash -# Output short and long options from man/usage text - -set -o errexit -o pipefail -o nounset - -main() { - declare tmpdir="_options.tmp" - declare tmp="${tmpdir}/${1:-generic}" - - mkdir -p "$tmpdir" - cat > "$tmp" - - # Do not propose single letter completions. It's not useful, it's noisy - # and it makes completion slower: - # Display all 200 possibilities? (y or n) - # sed -rn 's/.* (-[a-zA-Z0-9]).*/\1/gp' < "$tmp" - - # Options requiring an argument make that explicit by appending - # the equal sign (=) - sed -rn '/^\s+-/p' "$tmp" | - sed -rn 's/^\s{1,4}(-.)?\s{1,4}(--[a-zA-Z][-_a-zA-Z0-9]+=?).*/\2/p' -} - -main "$@" From b97c2043b8c0d5a9f40274fa0fedf30703511b8b Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Fri, 18 Dec 2020 06:36:29 +0100 Subject: [PATCH 62/82] Add install and uninstall build targets --- shell-completion/Makefile | 39 ++++++++++++++++++++++-- shell-completion/hledger-completion.bash | 8 ----- 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/shell-completion/Makefile b/shell-completion/Makefile index d821ec213..907a037a3 100644 --- a/shell-completion/Makefile +++ b/shell-completion/Makefile @@ -4,6 +4,22 @@ # get parallel jobs. MAKEFLAGS += --jobs=$(shell nproc 2>/dev/null || printf 8) +EUID := $(shell id -u) + +ifeq ($(EUID),0) +PREFIX := /usr/local +endif + +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 + +DESTDIR ?= + # Parse hledger's help and output all commands and command aliases in # parenthesis. Do not output single letter command aliases, it's not useful. COMMANDS_TMP := commands.tmp @@ -28,10 +44,29 @@ sort -u endef EXTENSIONS := ui web api +INSTALLED_EXTENSIONS := $(foreach EXT,$(EXTENSIONS),$(shell type hledger-$(EXT) >/dev/null 2>&1 && echo $(EXT))) -COMMANDS := $(sort $(shell $(PARSE_COMMANDS)) $(EXTENSIONS)) +COMMANDS := $(sort $(shell $(PARSE_COMMANDS)) $(INSTALLED_EXTENSIONS)) CMDOPTFILES := $(foreach CMD,$(COMMANDS),options-$(CMD).txt) +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 $(INSTALLED_EXTENSIONS); do \ + printf "symlink " ; \ + ln -sfv hledger "$(DESTDIR)$(BASHCOMPDIR)/hledger-$$ext" ; \ + done + +.PHONY: uninstall +uninstall: + @rm -vf "$(DESTDIR)$(BASHCOMPDIR)/hledger" + @for ext in $(INSTALLED_EXTENSIONS); do \ + rm -vf "$(DESTDIR)$(BASHCOMPDIR)/hledger-$$ext" ; \ + done + define M4DEPS := hledger-completion.bash.m4 \ hledger-completion.bash.stub \ @@ -42,8 +77,6 @@ generic-options.txt \ $(CMDOPTFILES) endef -all: hledger-completion.bash - hledger-completion.bash: $(M4DEPS) m4 hledger-completion.bash.m4 > $@ diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index 8a6db7e7c..9f3c0b200 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -417,7 +417,6 @@ read -r -d "" _hledger_complist_commands < Date: Fri, 18 Dec 2020 07:01:21 +0100 Subject: [PATCH 63/82] Modify .gitignore query-filters.txt is a build dependency and shouldn't be ignored. --- shell-completion/.gitignore | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/shell-completion/.gitignore b/shell-completion/.gitignore index 2211df63d..06e263d15 100644 --- a/shell-completion/.gitignore +++ b/shell-completion/.gitignore @@ -1 +1,4 @@ -*.txt +options*.txt +generic-options.txt +commands*.txt +commands*.tmp From b3f0b437a588948d6bc9ae2408bd36579f144edc Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Fri, 18 Dec 2020 08:25:17 +0100 Subject: [PATCH 64/82] Add example usage comment for debug targets --- shell-completion/Makefile | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/shell-completion/Makefile b/shell-completion/Makefile index 907a037a3..8cae609c9 100644 --- a/shell-completion/Makefile +++ b/shell-completion/Makefile @@ -98,12 +98,14 @@ clean: rm -f hledger-completion.bash -# Basic REGEX debugging targets -.PHONY: debug-commands debug-options +# Basic REGEX debugging targets. Example usage: +# diff <(make -s debug-options) <(make -s debug-options CMD=reg) +.PHONY: debug-commands debug-commands: $(PARSE_COMMANDS) +.PHONY: debug-options debug-options: CMD := debug-options: hledger $(CMD) -h | $(PARSE_OPTIONS) From effb1be0e64b8974627acedd2e9bf9baa89c49e6 Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Fri, 18 Dec 2020 08:39:54 +0100 Subject: [PATCH 65/82] Avoid hard-coded reference to `commands.tmp` --- shell-completion/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell-completion/Makefile b/shell-completion/Makefile index 8cae609c9..5149f4ec7 100644 --- a/shell-completion/Makefile +++ b/shell-completion/Makefile @@ -94,7 +94,7 @@ options-%.txt: .PHONY: clean clean: - rm -f commands*.{txt,tmp} generic-options.txt options-*.txt + rm -f $(COMMANDS_TMP) commands*.txt generic-options.txt options-*.txt rm -f hledger-completion.bash From 5c2dd6fa2f5f3b7c197856fd385a197cc36ada69 Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Fri, 18 Dec 2020 10:09:27 +0100 Subject: [PATCH 66/82] Move back commands/options parsing to separate shell scripts It made the Makefile more difficult to read and this way we can take advantage of `set -e`, `-o pipefail` and friends. Clean up debugging targets --- shell-completion/Makefile | 43 ++++-------------------------- shell-completion/parse-commands.sh | 13 +++++++++ shell-completion/parse-options.sh | 14 ++++++++++ 3 files changed, 32 insertions(+), 38 deletions(-) create mode 100755 shell-completion/parse-commands.sh create mode 100755 shell-completion/parse-options.sh diff --git a/shell-completion/Makefile b/shell-completion/Makefile index 5149f4ec7..b0a297dc7 100644 --- a/shell-completion/Makefile +++ b/shell-completion/Makefile @@ -20,28 +20,8 @@ endif DESTDIR ?= -# Parse hledger's help and output all commands and command aliases in -# parenthesis. Do not output single letter command aliases, it's not useful. -COMMANDS_TMP := commands.tmp - -define PARSE_COMMANDS := -hledger > $(COMMANDS_TMP) ; -{ \ - sed -rn 's/^\s+([a-z][-a-z]+)\s+.*/\1/p' $(COMMANDS_TMP) ; \ - sed -rn 's/^\s+[a-z][-a-z]+\s+\(([a-z][ ,a-z]+)\).*/\1/p' $(COMMANDS_TMP) | \ - sed 's/\s*,\s*/\n/g' | \ - sed '/^.$$/d' ; \ -} | sed '/^hledger/d' | sort -u -endef - -# 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 (=) -define PARSE_OPTIONS := -sed -rn '/^\s+-/p' | \ -sed -rn 's/^\s{1,4}(-.)?\s{1,4}(--[a-zA-Z][-_a-zA-Z0-9]+=?).*/\2/p' | \ -sort -u -endef +PARSE_COMMANDS := ./parse-commands.sh +PARSE_OPTIONS := ./parse-options.sh EXTENSIONS := ui web api INSTALLED_EXTENSIONS := $(foreach EXT,$(EXTENSIONS),$(shell type hledger-$(EXT) >/dev/null 2>&1 && echo $(EXT))) @@ -87,25 +67,12 @@ commands-list.txt: printf "%s," $(COMMANDS) | sed 's/,$$//' > $@ generic-options.txt: - hledger -h | $(PARSE_OPTIONS) > $@ + $(PARSE_OPTIONS) > $@ options-%.txt: - hledger $* -h | $(PARSE_OPTIONS) > $@ + $(PARSE_OPTIONS) $* > $@ .PHONY: clean clean: - rm -f $(COMMANDS_TMP) commands*.txt generic-options.txt options-*.txt + rm -f commands*.txt generic-options.txt options-*.txt rm -f hledger-completion.bash - - -# Basic REGEX debugging targets. Example usage: -# diff <(make -s debug-options) <(make -s debug-options CMD=reg) - -.PHONY: debug-commands -debug-commands: - $(PARSE_COMMANDS) - -.PHONY: debug-options -debug-options: CMD := -debug-options: - hledger $(CMD) -h | $(PARSE_OPTIONS) diff --git a/shell-completion/parse-commands.sh b/shell-completion/parse-commands.sh new file mode 100755 index 000000000..65489fdf9 --- /dev/null +++ b/shell-completion/parse-commands.sh @@ -0,0 +1,13 @@ +#!/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/^\s+([a-z][-a-z]+)\s+.*/\1/p' <<< "$commands_help" + sed -rn 's/^\s+[a-z][-a-z]+\s+\(([a-z][ ,a-z]+)\).*/\1/p' <<< "$commands_help" | + sed 's/\s*,\s*/\n/g' | + sed '/^.$/d' +} | sed '/^hledger/d' | sort -u diff --git a/shell-completion/parse-options.sh b/shell-completion/parse-options.sh new file mode 100755 index 000000000..3ba7f9ad9 --- /dev/null +++ b/shell-completion/parse-options.sh @@ -0,0 +1,14 @@ +#!/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 '/^\s+-/p' | + sed -rn 's/^\s{1,4}(-.)?\s{1,4}(--[a-zA-Z][-_a-zA-Z0-9]+=?).*/\2/p' | + sort -u From 9e90d25cce548d1b55b7aeda9ab6dec895192bad Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Fri, 18 Dec 2020 10:32:30 +0100 Subject: [PATCH 67/82] Add basic editor configuration to shell scripts --- shell-completion/parse-commands.sh | 7 +++++++ shell-completion/parse-options.sh | 13 ++++++++++--- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/shell-completion/parse-commands.sh b/shell-completion/parse-commands.sh index 65489fdf9..1ced05a77 100755 --- a/shell-completion/parse-commands.sh +++ b/shell-completion/parse-commands.sh @@ -11,3 +11,10 @@ commands_help=$(hledger) sed 's/\s*,\s*/\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 index 3ba7f9ad9..7d7636906 100755 --- a/shell-completion/parse-options.sh +++ b/shell-completion/parse-options.sh @@ -9,6 +9,13 @@ declare hledgerArgs=(--help) [[ -n $subcommand ]] && hledgerArgs=("$subcommand" "${hledgerArgs[@]}") hledger "${hledgerArgs[@]}" | - sed -rn '/^\s+-/p' | - sed -rn 's/^\s{1,4}(-.)?\s{1,4}(--[a-zA-Z][-_a-zA-Z0-9]+=?).*/\2/p' | - sort -u + sed -rn '/^\s+-/p' | + sed -rn 's/^\s{1,4}(-.)?\s{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 From b53e264da15ba10446fd7e50b34d6e820b4651e9 Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Fri, 18 Dec 2020 19:25:28 +0100 Subject: [PATCH 68/82] Update .gitignore Options/commands parsing no longer creates any temporary files --- shell-completion/.gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/shell-completion/.gitignore b/shell-completion/.gitignore index 06e263d15..d1772c642 100644 --- a/shell-completion/.gitignore +++ b/shell-completion/.gitignore @@ -1,4 +1,3 @@ options*.txt generic-options.txt commands*.txt -commands*.tmp From 0bb4d1a6eb7c6155b88d3864439b4ad4355a4ff4 Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Sat, 19 Dec 2020 07:54:36 +0100 Subject: [PATCH 69/82] Remove `api` from hledger extensions Superseded by hledger-web --- shell-completion/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell-completion/Makefile b/shell-completion/Makefile index b0a297dc7..05f08df00 100644 --- a/shell-completion/Makefile +++ b/shell-completion/Makefile @@ -23,7 +23,7 @@ DESTDIR ?= PARSE_COMMANDS := ./parse-commands.sh PARSE_OPTIONS := ./parse-options.sh -EXTENSIONS := ui web api +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)) From dea35043bdbe9a6ee7787df712127f519b72d35d Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Sat, 19 Dec 2020 08:11:55 +0100 Subject: [PATCH 70/82] Add phony clean-all build target A `make clean` before commit removes hledger-completion.bash and it is supposed to be in the repository. `make clean` removes build artifacts while keeping the latter. Do a `make clean-all` to purge everything. --- shell-completion/Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/shell-completion/Makefile b/shell-completion/Makefile index 05f08df00..a84e9a28b 100644 --- a/shell-completion/Makefile +++ b/shell-completion/Makefile @@ -75,4 +75,7 @@ options-%.txt: .PHONY: clean clean: rm -f commands*.txt generic-options.txt options-*.txt + +.PHONY: clean-all +clean-all: clean rm -f hledger-completion.bash From ac143aff6bd26429b943b088f0f14e6b46c71c32 Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Sat, 19 Dec 2020 08:30:32 +0100 Subject: [PATCH 71/82] Do not use `set -e` in helper shell scripts It is not a substitute for proper error checking, it can easily cause more trouble than good and it would be a burden for contributors and a source for potential misbehavior. See this take on the topic for example: http://mywiki.wooledge.org/BashFAQ/105 --- shell-completion/parse-commands.sh | 2 +- shell-completion/parse-options.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/shell-completion/parse-commands.sh b/shell-completion/parse-commands.sh index 1ced05a77..9274747ea 100755 --- a/shell-completion/parse-commands.sh +++ b/shell-completion/parse-commands.sh @@ -1,7 +1,7 @@ #!/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 +set -uo pipefail declare commands_help commands_help=$(hledger) diff --git a/shell-completion/parse-options.sh b/shell-completion/parse-options.sh index 7d7636906..80e0f6953 100755 --- a/shell-completion/parse-options.sh +++ b/shell-completion/parse-options.sh @@ -2,7 +2,7 @@ # 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 +set -uo pipefail declare subcommand=${1:-} declare hledgerArgs=(--help) From 62c3c8e6da85e070ec50075d299b74a1b9c273fb Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Sat, 19 Dec 2020 09:13:00 +0100 Subject: [PATCH 72/82] Exit build with an error if unable to parse hledger sub-commands --- shell-completion/Makefile | 5 +++++ shell-completion/parse-commands.sh | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/shell-completion/Makefile b/shell-completion/Makefile index a84e9a28b..eac81005b 100644 --- a/shell-completion/Makefile +++ b/shell-completion/Makefile @@ -27,6 +27,11 @@ 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) all: hledger-completion.bash diff --git a/shell-completion/parse-commands.sh b/shell-completion/parse-commands.sh index 9274747ea..19b5571d9 100755 --- a/shell-completion/parse-commands.sh +++ b/shell-completion/parse-commands.sh @@ -4,7 +4,7 @@ set -uo pipefail declare commands_help -commands_help=$(hledger) +commands_help=$(hledger) || exit { sed -rn 's/^\s+([a-z][-a-z]+)\s+.*/\1/p' <<< "$commands_help" sed -rn 's/^\s+[a-z][-a-z]+\s+\(([a-z][ ,a-z]+)\).*/\1/p' <<< "$commands_help" | From 0ca63ddc848953adfbb39a123cbf882021998e30 Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Sat, 19 Dec 2020 09:57:27 +0100 Subject: [PATCH 73/82] Install/uninstall completion for all extensions by default Install the symlinks unconditionally. This way the user don't need to reinstall completion after adding an extension. Of course fine- grained control is possible with: `make install EXTENSIONS=web` e.g. --- shell-completion/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shell-completion/Makefile b/shell-completion/Makefile index eac81005b..e61d71066 100644 --- a/shell-completion/Makefile +++ b/shell-completion/Makefile @@ -40,7 +40,7 @@ all: hledger-completion.bash install: @install -v -d "$(DESTDIR)$(BASHCOMPDIR)" @install -v -m 0644 hledger-completion.bash "$(DESTDIR)$(BASHCOMPDIR)/hledger" - @for ext in $(INSTALLED_EXTENSIONS); do \ + @for ext in $(EXTENSIONS); do \ printf "symlink " ; \ ln -sfv hledger "$(DESTDIR)$(BASHCOMPDIR)/hledger-$$ext" ; \ done @@ -48,7 +48,7 @@ install: .PHONY: uninstall uninstall: @rm -vf "$(DESTDIR)$(BASHCOMPDIR)/hledger" - @for ext in $(INSTALLED_EXTENSIONS); do \ + @for ext in $(EXTENSIONS); do \ rm -vf "$(DESTDIR)$(BASHCOMPDIR)/hledger-$$ext" ; \ done From 2a37479d60e76aedb83a494d00d872c1255ae1cd Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Sat, 19 Dec 2020 13:16:23 +0100 Subject: [PATCH 74/82] Revert "Do not use `set -e` in helper shell scripts" This reverts commit 0aeb88e663b9b19e173de20b132bef2189c6d7c9. --- shell-completion/parse-commands.sh | 4 ++-- shell-completion/parse-options.sh | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/shell-completion/parse-commands.sh b/shell-completion/parse-commands.sh index 19b5571d9..1ced05a77 100755 --- a/shell-completion/parse-commands.sh +++ b/shell-completion/parse-commands.sh @@ -1,10 +1,10 @@ #!/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 -uo pipefail +set -euo pipefail declare commands_help -commands_help=$(hledger) || exit +commands_help=$(hledger) { sed -rn 's/^\s+([a-z][-a-z]+)\s+.*/\1/p' <<< "$commands_help" sed -rn 's/^\s+[a-z][-a-z]+\s+\(([a-z][ ,a-z]+)\).*/\1/p' <<< "$commands_help" | diff --git a/shell-completion/parse-options.sh b/shell-completion/parse-options.sh index 80e0f6953..7d7636906 100755 --- a/shell-completion/parse-options.sh +++ b/shell-completion/parse-options.sh @@ -2,7 +2,7 @@ # 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 -uo pipefail +set -euo pipefail declare subcommand=${1:-} declare hledgerArgs=(--help) From 2e44fe9e90374b2fd36ff96b8c11de3b83968f93 Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Sat, 19 Dec 2020 16:13:31 +0100 Subject: [PATCH 75/82] Portability: replace GNU extension `\s` with `[[:space:]]` --- shell-completion/parse-commands.sh | 6 +++--- shell-completion/parse-options.sh | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/shell-completion/parse-commands.sh b/shell-completion/parse-commands.sh index 1ced05a77..644a9936f 100755 --- a/shell-completion/parse-commands.sh +++ b/shell-completion/parse-commands.sh @@ -6,9 +6,9 @@ set -euo pipefail declare commands_help commands_help=$(hledger) { - sed -rn 's/^\s+([a-z][-a-z]+)\s+.*/\1/p' <<< "$commands_help" - sed -rn 's/^\s+[a-z][-a-z]+\s+\(([a-z][ ,a-z]+)\).*/\1/p' <<< "$commands_help" | - sed 's/\s*,\s*/\n/g' | + 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 diff --git a/shell-completion/parse-options.sh b/shell-completion/parse-options.sh index 7d7636906..bc1495479 100755 --- a/shell-completion/parse-options.sh +++ b/shell-completion/parse-options.sh @@ -9,8 +9,8 @@ declare hledgerArgs=(--help) [[ -n $subcommand ]] && hledgerArgs=("$subcommand" "${hledgerArgs[@]}") hledger "${hledgerArgs[@]}" | - sed -rn '/^\s+-/p' | - sed -rn 's/^\s{1,4}(-.)?\s{1,4}(--[a-zA-Z][-_a-zA-Z0-9]+=?).*/\2/p' | + 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: From 419817e65607141997ec2701c672bc2fdd805dc1 Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Sun, 20 Dec 2020 07:56:47 +0100 Subject: [PATCH 76/82] Prevent unwanted m4 macro expansion + `-g` GNU compatibility flag Most of the included files are meant to be output literally, without any macro processing. Using `undivert` in place of `include` achieves that. This is a safety net against unsanitized input generated during the build. Also, developers editing the shell code stub shouldn't be constantly alert about triggering accidental macro expansion. In order that `undivert` mimics GNU behaviour on BSDs, the `-g` flag is used for the m4 invocation. --- shell-completion/Makefile | 2 +- shell-completion/hledger-completion.bash.m4 | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/shell-completion/Makefile b/shell-completion/Makefile index e61d71066..9c9deccdd 100644 --- a/shell-completion/Makefile +++ b/shell-completion/Makefile @@ -63,7 +63,7 @@ $(CMDOPTFILES) endef hledger-completion.bash: $(M4DEPS) - m4 hledger-completion.bash.m4 > $@ + m4 -g hledger-completion.bash.m4 > $@ commands.txt: printf "%s\n" $(COMMANDS) > $@ diff --git a/shell-completion/hledger-completion.bash.m4 b/shell-completion/hledger-completion.bash.m4 index a5636417c..4e4685944 100644 --- a/shell-completion/hledger-completion.bash.m4 +++ b/shell-completion/hledger-completion.bash.m4 @@ -1,19 +1,19 @@ -include(`hledger-completion.bash.stub')dnl +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. read -r -d "" _hledger_complist_commands < Date: Sun, 20 Dec 2020 09:04:59 +0100 Subject: [PATCH 77/82] BSDmakefile Just print a reminder to use `gmake` instead of spurting a bunch of errors. --- shell-completion/BSDmakefile | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 shell-completion/BSDmakefile 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" From 3ab796fd9e4932c46f2023ddcdf2f85a929aa08b Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Sun, 20 Dec 2020 09:52:17 +0100 Subject: [PATCH 78/82] Update README --- shell-completion/README.md | 47 ++++++++++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/shell-completion/README.md b/shell-completion/README.md index 9804b608c..2df94b343 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: +To install from the repository, do: -- Download or copy the file `shell-completion/hledger-completion.bash` and save - it as `~/.hledger-completion.bash`. +``` sh +cd /path-to-repo/shell-completion +make install +``` -- Add the command `source ~/.hledger-completion.bash` this to the end of your - `~/.bashrc` file. +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: -- Then, you have to start a new Bash, e.g. by typing `bash` on the current - shell. +- $BASH_COMPLETION_USER_DIR/completions +- $XDG_DATA_HOME/bash-completion/completions +- $HOME/.local/share/bash-completion/completions +- etc. -Example installation script: +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: ``` -cp hledger-completion.bash ~/.hledger-completion.bash -echo 'source ~/.hledger-completion.bash' >> ~/.bashrc +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 From 36fca6540491918bf8fb489fa8b943206b2c90ec Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Sun, 20 Dec 2020 10:05:41 +0100 Subject: [PATCH 79/82] README: fix syntax highlighting, quotes --- shell-completion/README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/shell-completion/README.md b/shell-completion/README.md index 2df94b343..26204a517 100644 --- a/shell-completion/README.md +++ b/shell-completion/README.md @@ -29,7 +29,7 @@ distribution. Refer to the last paragraph of this section for how to test that. To install from the repository, do: -``` sh +```sh cd /path-to-repo/shell-completion make install ``` @@ -38,9 +38,9 @@ Completions installed this way will be loaded dynamically after you use the hled 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 +- `$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 @@ -48,7 +48,7 @@ You can manually achieve the effects of `make install` by copying to `hledger`, `_hledger` or `hledger.bash`. For the gory details, type this in a bash shell: -``` sh +```sh type __load_completion ``` @@ -59,7 +59,7 @@ 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 @@ -86,7 +86,7 @@ Information for developers Generate the completion script for Bash: -``` +```sh # change into this folder: cd shell-completion/ make @@ -102,7 +102,7 @@ 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 ``` From 3d3cd2d4a2b3b0341648d59c07efa02c723d99ec Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Sun, 20 Dec 2020 10:35:53 +0100 Subject: [PATCH 80/82] Makefile: move all variable definitions to the top, before targets --- shell-completion/Makefile | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/shell-completion/Makefile b/shell-completion/Makefile index 9c9deccdd..257e34297 100644 --- a/shell-completion/Makefile +++ b/shell-completion/Makefile @@ -34,6 +34,17 @@ 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 @@ -52,16 +63,6 @@ uninstall: rm -vf "$(DESTDIR)$(BASHCOMPDIR)/hledger-$$ext" ; \ done -define M4DEPS := -hledger-completion.bash.m4 \ -hledger-completion.bash.stub \ -commands.txt \ -commands-list.txt \ -query-filters.txt \ -generic-options.txt \ -$(CMDOPTFILES) -endef - hledger-completion.bash: $(M4DEPS) m4 -g hledger-completion.bash.m4 > $@ From 066fa964b6079283558f52d3da09c61aabecccfe Mon Sep 17 00:00:00 2001 From: Vladimir Zhelezov Date: Mon, 21 Dec 2020 13:35:40 +0100 Subject: [PATCH 81/82] Disable shell expansion in here-docs Better be safe that sorry... --- shell-completion/hledger-completion.bash | 178 +++++++++--------- shell-completion/hledger-completion.bash.m4 | 16 +- shell-completion/hledger-completion.bash.stub | 2 +- 3 files changed, 98 insertions(+), 98 deletions(-) diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index 9f3c0b200..dfc51154f 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -1,5 +1,5 @@ # -*- mode: sh; sh-basic-offset: 4; indent-tabs-mode: nil -*- -# ex: ts=4 sw=4 et +# ex: ft=sh ts=4 sw=4 et # shellcheck disable=2034,2154 # Completion script for hledger. @@ -413,7 +413,7 @@ _hledger() { # m4 macro processor. # Included files must have exactly one newline at EOF to prevent weired errors. -read -r -d "" _hledger_complist_commands < Date: Sun, 28 Feb 2021 08:36:42 +0100 Subject: [PATCH 82/82] Rebuild completion after the rebase on upstream/master * `help` command's output is no longer listing help topics, so those completions are removed. * `check` command supersedes `check-dates`, `check-dupes`, etc. --- shell-completion/hledger-completion.bash | 325 ++++++++++-------- shell-completion/hledger-completion.bash.stub | 9 +- 2 files changed, 179 insertions(+), 155 deletions(-) diff --git a/shell-completion/hledger-completion.bash b/shell-completion/hledger-completion.bash index dfc51154f..9599adcb7 100644 --- a/shell-completion/hledger-completion.bash +++ b/shell-completion/hledger-completion.bash @@ -102,15 +102,8 @@ _hledger_completion() { # Subcommand specific case $subcommand in - help) - compopt -o nosort +o filenames - _hledger_compreply "$( - compgen -W "$(hledger help | tail -n 1)" -- "$cur" - )" - return 0 - ;; # These do not expect or support any query arguments - commodities|check-dupes|files|import|print-unique|test) + commodities|check|files|help|import|print-unique|test) return 0 ;; esac @@ -427,14 +420,12 @@ bs bse cashflow cf -check-dates -check-dupes +check close codes commodities descriptions diff -equity files help import @@ -453,8 +444,8 @@ roi stats tags test -txns ui +web __TEXT__ read -r -d "" _hledger_complist_query_filters <<"__TEXT__" @@ -495,6 +486,8 @@ read -r -d "" _hledger_complist_generic_options <<"__TEXT__" --help --ignore-assertions --infer-market-price +--info +--man --market --monthly --pending @@ -503,6 +496,7 @@ read -r -d "" _hledger_complist_generic_options <<"__TEXT__" --quarterly --real --rules-file= +--strict --unmarked --value= --version @@ -535,7 +529,9 @@ read -r -d "" _hledger_complist_options_accounts <<"__TEXT__" --forecast --help --ignore-assertions ---infer-value +--infer-market-price +--info +--man --market --monthly --pending @@ -544,6 +540,7 @@ read -r -d "" _hledger_complist_options_accounts <<"__TEXT__" --quarterly --real --rules-file= +--strict --tree --unmarked --used @@ -573,6 +570,8 @@ read -r -d "" _hledger_complist_options_activity <<"__TEXT__" --help --ignore-assertions --infer-market-price +--info +--man --market --monthly --pending @@ -581,6 +580,7 @@ read -r -d "" _hledger_complist_options_activity <<"__TEXT__" --quarterly --real --rules-file= +--strict --unmarked --value= --version @@ -595,9 +595,12 @@ read -r -d "" _hledger_complist_options_add <<"__TEXT__" --file= --help --ignore-assertions +--info +--man --no-new-accounts --pivot= --rules-file= +--strict --version __TEXT__ @@ -621,6 +624,8 @@ read -r -d "" _hledger_complist_options_areg <<"__TEXT__" --help --ignore-assertions --infer-market-price +--info +--man --market --monthly --no-elide @@ -632,6 +637,7 @@ read -r -d "" _hledger_complist_options_areg <<"__TEXT__" --quarterly --real --rules-file= +--strict --txn-dates --unmarked --value= @@ -661,6 +667,8 @@ read -r -d "" _hledger_complist_options_aregister <<"__TEXT__" --help --ignore-assertions --infer-market-price +--info +--man --market --monthly --no-elide @@ -672,6 +680,7 @@ read -r -d "" _hledger_complist_options_aregister <<"__TEXT__" --quarterly --real --rules-file= +--strict --txn-dates --unmarked --value= @@ -708,8 +717,10 @@ read -r -d "" _hledger_complist_options_bal <<"__TEXT__" --help --historical --ignore-assertions ---infer-value +--infer-market-price +--info --invert +--man --market --monthly --no-elide @@ -726,6 +737,7 @@ read -r -d "" _hledger_complist_options_bal <<"__TEXT__" --row-total --rules-file= --sort-amount +--strict --transpose --tree --unmarked @@ -762,8 +774,10 @@ read -r -d "" _hledger_complist_options_balance <<"__TEXT__" --help --historical --ignore-assertions ---infer-value +--infer-market-price +--info --invert +--man --market --monthly --no-elide @@ -780,6 +794,7 @@ read -r -d "" _hledger_complist_options_balance <<"__TEXT__" --row-total --rules-file= --sort-amount +--strict --transpose --tree --unmarked @@ -816,6 +831,8 @@ read -r -d "" _hledger_complist_options_balancesheet <<"__TEXT__" --historical --ignore-assertions --infer-market-price +--info +--man --market --monthly --no-elide @@ -832,6 +849,7 @@ read -r -d "" _hledger_complist_options_balancesheet <<"__TEXT__" --row-total --rules-file= --sort-amount +--strict --tree --unmarked --value= @@ -867,6 +885,8 @@ read -r -d "" _hledger_complist_options_balancesheetequity <<"__TEXT__" --historical --ignore-assertions --infer-market-price +--info +--man --market --monthly --no-elide @@ -883,6 +903,7 @@ read -r -d "" _hledger_complist_options_balancesheetequity <<"__TEXT__" --row-total --rules-file= --sort-amount +--strict --tree --unmarked --value= @@ -918,6 +939,8 @@ read -r -d "" _hledger_complist_options_bs <<"__TEXT__" --historical --ignore-assertions --infer-market-price +--info +--man --market --monthly --no-elide @@ -934,6 +957,7 @@ read -r -d "" _hledger_complist_options_bs <<"__TEXT__" --row-total --rules-file= --sort-amount +--strict --tree --unmarked --value= @@ -969,6 +993,8 @@ read -r -d "" _hledger_complist_options_bse <<"__TEXT__" --historical --ignore-assertions --infer-market-price +--info +--man --market --monthly --no-elide @@ -985,6 +1011,7 @@ read -r -d "" _hledger_complist_options_bse <<"__TEXT__" --row-total --rules-file= --sort-amount +--strict --tree --unmarked --value= @@ -1020,6 +1047,8 @@ read -r -d "" _hledger_complist_options_cashflow <<"__TEXT__" --historical --ignore-assertions --infer-market-price +--info +--man --market --monthly --no-elide @@ -1036,6 +1065,7 @@ read -r -d "" _hledger_complist_options_cashflow <<"__TEXT__" --row-total --rules-file= --sort-amount +--strict --tree --unmarked --value= @@ -1071,6 +1101,8 @@ read -r -d "" _hledger_complist_options_cf <<"__TEXT__" --historical --ignore-assertions --infer-market-price +--info +--man --market --monthly --no-elide @@ -1087,6 +1119,7 @@ read -r -d "" _hledger_complist_options_cf <<"__TEXT__" --row-total --rules-file= --sort-amount +--strict --tree --unmarked --value= @@ -1095,43 +1128,7 @@ read -r -d "" _hledger_complist_options_cf <<"__TEXT__" --yearly __TEXT__ -read -r -d "" _hledger_complist_options_check_dates <<"__TEXT__" ---alias= ---anon ---auto ---begin= ---cleared ---color= ---cost ---daily ---date2 ---debug= ---depth= ---empty ---end= ---exchange= ---file= ---forecast ---help ---ignore-assertions ---infer-value ---market ---monthly ---pending ---period= ---pivot= ---quarterly ---real ---rules-file= ---strict ---unmarked ---value= ---version ---weekly ---yearly -__TEXT__ - -read -r -d "" _hledger_complist_options_check_dupes <<"__TEXT__" +read -r -d "" _hledger_complist_options_check <<"__TEXT__" --alias= --anon --auto @@ -1151,6 +1148,8 @@ read -r -d "" _hledger_complist_options_check_dupes <<"__TEXT__" --help --ignore-assertions --infer-market-price +--info +--man --market --monthly --pending @@ -1159,6 +1158,7 @@ read -r -d "" _hledger_complist_options_check_dupes <<"__TEXT__" --quarterly --real --rules-file= +--strict --unmarked --value= --version @@ -1189,8 +1189,10 @@ read -r -d "" _hledger_complist_options_close <<"__TEXT__" --forecast --help --ignore-assertions ---infer-value +--infer-market-price +--info --interleaved +--man --market --monthly --open @@ -1203,6 +1205,7 @@ read -r -d "" _hledger_complist_options_close <<"__TEXT__" --real --rules-file= --show-costs +--strict --unmarked --value= --version @@ -1229,7 +1232,9 @@ read -r -d "" _hledger_complist_options_codes <<"__TEXT__" --forecast --help --ignore-assertions ---infer-value +--infer-market-price +--info +--man --market --monthly --pending @@ -1238,6 +1243,7 @@ read -r -d "" _hledger_complist_options_codes <<"__TEXT__" --quarterly --real --rules-file= +--strict --unmarked --value= --version @@ -1252,8 +1258,11 @@ read -r -d "" _hledger_complist_options_commodities <<"__TEXT__" --file= --help --ignore-assertions +--info +--man --pivot= --rules-file= +--strict --version __TEXT__ @@ -1277,6 +1286,8 @@ read -r -d "" _hledger_complist_options_descriptions <<"__TEXT__" --help --ignore-assertions --infer-market-price +--info +--man --market --monthly --pending @@ -1285,6 +1296,7 @@ read -r -d "" _hledger_complist_options_descriptions <<"__TEXT__" --quarterly --real --rules-file= +--strict --unmarked --value= --version @@ -1299,55 +1311,14 @@ read -r -d "" _hledger_complist_options_diff <<"__TEXT__" --file= --help --ignore-assertions +--info +--man --pivot= --rules-file= +--strict --version __TEXT__ -read -r -d "" _hledger_complist_options_equity <<"__TEXT__" ---alias= ---anon ---auto ---begin= ---cleared ---close ---close-acct= ---close-desc= ---color= ---cost ---daily ---date2 ---debug= ---depth= ---empty ---end= ---exchange= ---explicit ---file= ---forecast ---help ---ignore-assertions ---infer-value ---interleaved ---market ---monthly ---open ---open-acct= ---open-desc= ---pending ---period= ---pivot= ---quarterly ---real ---rules-file= ---show-costs ---unmarked ---value= ---version ---weekly ---yearly -__TEXT__ - read -r -d "" _hledger_complist_options_files <<"__TEXT__" --alias= --anon @@ -1355,17 +1326,16 @@ read -r -d "" _hledger_complist_options_files <<"__TEXT__" --file= --help --ignore-assertions +--info +--man --pivot= --rules-file= +--strict --version __TEXT__ read -r -d "" _hledger_complist_options_help <<"__TEXT__" ---cat --help ---info ---man ---pager __TEXT__ read -r -d "" _hledger_complist_options_import <<"__TEXT__" @@ -1390,6 +1360,8 @@ read -r -d "" _hledger_complist_options_import <<"__TEXT__" --help --ignore-assertions --infer-market-price +--info +--man --market --monthly --pending @@ -1398,6 +1370,7 @@ read -r -d "" _hledger_complist_options_import <<"__TEXT__" --quarterly --real --rules-file= +--strict --unmarked --value= --version @@ -1432,6 +1405,8 @@ read -r -d "" _hledger_complist_options_incomestatement <<"__TEXT__" --historical --ignore-assertions --infer-market-price +--info +--man --market --monthly --no-elide @@ -1448,6 +1423,7 @@ read -r -d "" _hledger_complist_options_incomestatement <<"__TEXT__" --row-total --rules-file= --sort-amount +--strict --tree --unmarked --value= @@ -1482,7 +1458,9 @@ read -r -d "" _hledger_complist_options_is <<"__TEXT__" --help --historical --ignore-assertions ---infer-value +--infer-market-price +--info +--man --market --monthly --no-elide @@ -1499,6 +1477,7 @@ read -r -d "" _hledger_complist_options_is <<"__TEXT__" --row-total --rules-file= --sort-amount +--strict --tree --unmarked --value= @@ -1527,6 +1506,8 @@ read -r -d "" _hledger_complist_options_notes <<"__TEXT__" --help --ignore-assertions --infer-market-price +--info +--man --market --monthly --pending @@ -1535,6 +1516,7 @@ read -r -d "" _hledger_complist_options_notes <<"__TEXT__" --quarterly --real --rules-file= +--strict --unmarked --value= --version @@ -1553,6 +1535,7 @@ read -r -d "" _hledger_complist_options_payees <<"__TEXT__" --daily --date2 --debug= +--declared --depth= --empty --end= @@ -1562,6 +1545,8 @@ read -r -d "" _hledger_complist_options_payees <<"__TEXT__" --help --ignore-assertions --infer-market-price +--info +--man --market --monthly --pending @@ -1570,7 +1555,9 @@ read -r -d "" _hledger_complist_options_payees <<"__TEXT__" --quarterly --real --rules-file= +--strict --unmarked +--used --value= --version --weekly @@ -1597,8 +1584,10 @@ read -r -d "" _hledger_complist_options_prices <<"__TEXT__" --forecast --help --ignore-assertions ---infer-value +--infer-market-price +--info --inverted-costs +--man --market --monthly --pending @@ -1607,6 +1596,7 @@ read -r -d "" _hledger_complist_options_prices <<"__TEXT__" --quarterly --real --rules-file= +--strict --unmarked --value= --version @@ -1635,6 +1625,8 @@ read -r -d "" _hledger_complist_options_print <<"__TEXT__" --help --ignore-assertions --infer-market-price +--info +--man --market --match= --monthly @@ -1647,6 +1639,7 @@ read -r -d "" _hledger_complist_options_print <<"__TEXT__" --quarterly --real --rules-file= +--strict --unmarked --value= --version @@ -1674,6 +1667,8 @@ read -r -d "" _hledger_complist_options_print_unique <<"__TEXT__" --help --ignore-assertions --infer-market-price +--info +--man --market --monthly --pending @@ -1682,6 +1677,7 @@ read -r -d "" _hledger_complist_options_print_unique <<"__TEXT__" --quarterly --real --rules-file= +--strict --unmarked --value= --version @@ -1711,8 +1707,10 @@ read -r -d "" _hledger_complist_options_reg <<"__TEXT__" --help --historical --ignore-assertions ---infer-value +--infer-market-price +--info --invert +--man --market --monthly --output-file= @@ -1724,6 +1722,7 @@ read -r -d "" _hledger_complist_options_reg <<"__TEXT__" --real --related --rules-file= +--strict --unmarked --value= --version @@ -1754,8 +1753,10 @@ read -r -d "" _hledger_complist_options_register <<"__TEXT__" --help --historical --ignore-assertions ---infer-value +--infer-market-price +--info --invert +--man --market --monthly --output-file= @@ -1767,6 +1768,7 @@ read -r -d "" _hledger_complist_options_register <<"__TEXT__" --real --related --rules-file= +--strict --unmarked --value= --version @@ -1795,6 +1797,8 @@ read -r -d "" _hledger_complist_options_register_match <<"__TEXT__" --help --ignore-assertions --infer-market-price +--info +--man --market --monthly --pending @@ -1803,6 +1807,7 @@ read -r -d "" _hledger_complist_options_register_match <<"__TEXT__" --quarterly --real --rules-file= +--strict --unmarked --value= --version @@ -1832,6 +1837,8 @@ read -r -d "" _hledger_complist_options_rewrite <<"__TEXT__" --help --ignore-assertions --infer-market-price +--info +--man --market --monthly --pending @@ -1840,6 +1847,7 @@ read -r -d "" _hledger_complist_options_rewrite <<"__TEXT__" --quarterly --real --rules-file= +--strict --unmarked --value= --version @@ -1867,8 +1875,10 @@ read -r -d "" _hledger_complist_options_roi <<"__TEXT__" --forecast --help --ignore-assertions ---infer-value +--infer-market-price +--info --investment= +--man --market --monthly --pending @@ -1878,6 +1888,7 @@ read -r -d "" _hledger_complist_options_roi <<"__TEXT__" --quarterly --real --rules-file= +--strict --unmarked --value= --version @@ -1905,6 +1916,8 @@ read -r -d "" _hledger_complist_options_stats <<"__TEXT__" --help --ignore-assertions --infer-market-price +--info +--man --market --monthly --output-file= @@ -1914,6 +1927,7 @@ read -r -d "" _hledger_complist_options_stats <<"__TEXT__" --quarterly --real --rules-file= +--strict --unmarked --value= --version @@ -1941,6 +1955,8 @@ read -r -d "" _hledger_complist_options_tags <<"__TEXT__" --help --ignore-assertions --infer-market-price +--info +--man --market --monthly --parsed @@ -1950,6 +1966,7 @@ read -r -d "" _hledger_complist_options_tags <<"__TEXT__" --quarterly --real --rules-file= +--strict --unmarked --value= --values @@ -1961,49 +1978,11 @@ __TEXT__ read -r -d "" _hledger_complist_options_test <<"__TEXT__" --debug= --help +--info +--man --version __TEXT__ -read -r -d "" _hledger_complist_options_txns <<"__TEXT__" ---alias= ---anon ---auto ---begin= ---cleared ---color= ---cost ---daily ---date2 ---debug= ---depth= ---empty ---end= ---exchange= ---explicit ---file= ---forecast ---help ---ignore-assertions ---infer-market-price ---market ---match= ---monthly ---new ---output-file= ---output-format= ---pending ---period= ---pivot= ---quarterly ---real ---rules-file= ---unmarked ---value= ---version ---weekly ---yearly -__TEXT__ - read -r -d "" _hledger_complist_options_ui <<"__TEXT__" --alias= --anon @@ -2026,6 +2005,8 @@ read -r -d "" _hledger_complist_options_ui <<"__TEXT__" --help --ignore-assertions --infer-market-price +--info +--man --market --monthly --pending @@ -2035,6 +2016,7 @@ read -r -d "" _hledger_complist_options_ui <<"__TEXT__" --real --register= --rules-file= +--strict --theme= --tree --unmarked @@ -2045,4 +2027,53 @@ read -r -d "" _hledger_complist_options_ui <<"__TEXT__" --yearly __TEXT__ +read -r -d "" _hledger_complist_options_web <<"__TEXT__" +--alias= +--anon +--auto +--base-url= +--begin= +--capabilities= +--capabilities-header= +--cleared +--color= +--cors= +--cost +--daily +--date2 +--debug= +--depth= +--empty +--end= +--exchange= +--file= +--file-url= +--forecast +--help +--host= +--ignore-assertions +--infer-market-price +--info +--man +--market +--monthly +--pending +--period= +--pivot= +--port= +--quarterly +--real +--rules-file= +--serve +--serve-api +--socket= +--strict +--test +--unmarked +--value= +--version +--weekly +--yearly +__TEXT__ + return 0 diff --git a/shell-completion/hledger-completion.bash.stub b/shell-completion/hledger-completion.bash.stub index 84accf116..2c4862615 100644 --- a/shell-completion/hledger-completion.bash.stub +++ b/shell-completion/hledger-completion.bash.stub @@ -102,15 +102,8 @@ _hledger_completion() { # Subcommand specific case $subcommand in - help) - compopt -o nosort +o filenames - _hledger_compreply "$( - compgen -W "$(hledger help | tail -n 1)" -- "$cur" - )" - return 0 - ;; # These do not expect or support any query arguments - commodities|check-dupes|files|import|print-unique|test) + commodities|check|files|help|import|print-unique|test) return 0 ;; esac