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().
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 (:).
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.
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.
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...
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.
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.
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 > <TAB>
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.
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.