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.
This commit is contained in:
parent
bc66b23520
commit
bb8118c771
@ -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 <<TEXT
|
||||
accounts
|
||||
activity
|
||||
add
|
||||
api
|
||||
areg
|
||||
aregister
|
||||
bal
|
||||
@ -454,7 +457,6 @@ test
|
||||
txns
|
||||
ui
|
||||
web
|
||||
api
|
||||
TEXT
|
||||
|
||||
read -r -d "" _hledger_complist_query_filters <<TEXT
|
||||
@ -601,6 +603,9 @@ read -r -d "" _hledger_complist_options_add <<TEXT
|
||||
--version
|
||||
TEXT
|
||||
|
||||
read -r -d "" _hledger_complist_options_api <<TEXT
|
||||
TEXT
|
||||
|
||||
read -r -d "" _hledger_complist_options_areg <<TEXT
|
||||
--alias=
|
||||
--anon
|
||||
@ -2048,13 +2053,4 @@ TEXT
|
||||
read -r -d "" _hledger_complist_options_web <<TEXT
|
||||
TEXT
|
||||
|
||||
read -r -d "" _hledger_complist_options_api <<TEXT
|
||||
TEXT
|
||||
|
||||
return 0
|
||||
|
||||
# Local Variables:
|
||||
# sh-basic-offset: 4
|
||||
# indent-tabs-mode: nil
|
||||
# End:
|
||||
# ex: ts=4 sw=4 et
|
||||
|
||||
@ -1,411 +1,4 @@
|
||||
# shellcheck disable=2034
|
||||
|
||||
# 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=<TAB>, 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
|
||||
|
||||
410
shell-completion/hledger-completion.bash.stub
Normal file
410
shell-completion/hledger-completion.bash.stub
Normal file
@ -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=<TAB>, 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
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user