hledger/shell-completion
Vladimir Zhelezov 72b737a42f 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.
2021-02-28 08:33:16 +01:00
..
.gitignore Update README and .gitignore 2019-01-23 16:46:17 -08:00
foreach2.m4 Add files for bash completion 2019-01-23 16:46:17 -08:00
hledger-completion.bash Fix #1404, and more... 2021-02-28 08:33:16 +01:00
hledger-completion.bash.m4 Fix #1404, and more... 2021-02-28 08:33:16 +01:00
Makefile Fix #1404, and more... 2021-02-28 08:33:16 +01:00
output-commands.sh Fix #1404, and more... 2021-02-28 08:33:16 +01:00
output-options.sh Fix #1404, and more... 2021-02-28 08:33:16 +01:00
query-filters.txt Fix #1404, and more... 2021-02-28 08:33:16 +01:00
quote.m4 Add files for bash completion 2019-01-23 16:46:17 -08:00
README.md doc: fix formatting issue 2020-06-16 19:13:18 -07:00

Shell completion for CLI

This code generates shell completion scripts for hledger's command line interface. Shell completion is usually triggered by pressing the tab key once or twice after typing the command hledger . (The exact behavior may differ in shells other than Bash.)

Currently, only Bash is supported but Zsh or Fish can be added.

asciicast

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)
  • query filter keywords like status:, tag:, or amt:

Installation for end users

Completions are currently only implemented for the Bash shell.

Please check first if the completions for hledger are already installed on your distribution. Refer to the last paragraph of this section for how to test that.

To install the completions manually, follow this steps:

  • Download or copy the file shell-completion/hledger-completion.bash and save it as ~/.hledger-completion.bash.

  • Add the command source ~/.hledger-completion.bash this to the end of your ~/.bashrc file.

  • Then, you have to start a new Bash, e.g. by typing bash on the current shell.

Example installation script:

cp hledger-completion.bash ~/.hledger-completion.bash
echo 'source ~/.hledger-completion.bash' >> ~/.bashrc

Now, try it by typing hledger (with a space after the command) and press the tab key twice. You should see a list of appropriate completions for hledger. Then you can type a part of one of the suggestions and press tab again to complete it.

Background

The Bash completion script is generated (GNU make) by parsing output of hledger, hledger -h, and hledger <cmd> -h. The script also uses hledger accounts for account name completion. I propose that the Makefile is not run at every built but rather manually when the CLI changes.

Information for developers

Generate the completion script for Bash:

# change into this folder:
cd shell-completion/
make

Hint: GNU make, GNU m4, and GNU parallel must be installed to call make. The first two usually are.

The generated completion script must be installed. The package maintainer for your distribution should be responsible for this.

For now, or to live-test the script, you can use these two commands:

ln -s hledger-completion.bash ~/.hledger-completion.bash
echo 'source ~/.hledger-completion.bash' >> ~/.bashrc

After that, you have to start a new Bash, e.g. by typing bash on the current shell.

Now, try it by typing hledger (with a space after the command) and press the tab key twice. You know how completions work if not, see above in the Installation section.

Completion scripts for other shells (e.g. Fish or Zsh)

You're welcome to add completion scripts for other shells. It should not be too hard! All available hledger options and commands are already there (generated by the Makefile).

The generated text files with options and commands are: commands.txt, generic-options.txt, and options-*.txt where * is the subcommand.

Instructions to add support for another shell:

  1. Create e.g. hledger-completion.fish.m4 as a template file.

  2. Add a Make rule to transform it to hledger-completion.fish.

  3. Use m4 commands to include hledger options and commands into your script template. See hledger-completion.bash.m4 as a reference.

  4. Use make and then make hledger-completion.fish to create and test the completion script.

  5. Finally, if everything is working, also add the generated artifact hledger-completion.fish to the repo so that people can use it directly.