## Value Proposition `complgen` is a command line program that takes a `.usage` (see [examples](examples/)) file as input and outputs a standalone, distributable shell completion script. One grammar file can be compiled for popular shells like bash/fish/zsh/pwsh, freeing you from having to reimplement logic for each of the shells separately. `complgen` grammars are declarative and shell-agnostic, unless you need to reach for a shell-specific behavior. `complgen`'s strong suit is in handling complex subcommands completion and sourcing candidates from external, run-time shell commands. ## Demo ![demo](https://github.com/adaszko/complgen/blob/demo/complgen.gif) ## Usage ### Bash
```sh $ cat hello.usage hello --color=(always | never | auto); $ complgen --bash hello.bash hello.usage $ source hello.bash $ hello --color= always auto never ```
### Fish
```sh $ cat hello.usage hello --color=(always | never | auto); $ complgen --fish hello.fish hello.usage $ source hello.fish $ hello --color= --color=always --color=auto --color=never ```
### Zsh
```sh % cat hello.usage hello --color=(always | never | auto); % complgen --zsh _hello hello.usage % source _hello % hello --color= always auto never ``` 💡 Note: Under ZSH, `source` isn't strictly necessary — it is enough to put the output file in one of the directories listed in `$fpath` variable.
### PowerShell
```powershell PS> Get-Content hello.usage hello --color=(always | never | auto); PS> complgen --pwsh hello.ps1 hello.usage PS> . ./hello.ps1 PS> hello --color= --color=always --color=auto --color=never ``` 💡 Note: Requires PowerShell Core 7+ (pwsh). Add the script to your `$PROFILE` for persistent completions.
## Installation ```sh cargo install --git https://github.com/adaszko/complgen --tag v0.11.0 complgen ``` Or download a pre-built binary from [the latest GitHub release](https://github.com/adaszko/complgen/releases). ## Syntax See the [`examples` subdirectory](examples/). complgen's grammar is based on [compleat](https://github.com/mbrubeck/compleat/blob/master/README.markdown#syntax)'s one. A grammar is a series of lines separated by a semicolon (`;`). Each line either represents a single variant of invoking the completed command (e.g. `git status; git log`) or is a nonterminal definition (` = bar`). * `a b` matches `a` followed by `b`. * `a b | c` matches either `a b` or `c` (IOW: sequence binds stronger than alternative). * `[a]` matches zero or one occurrences of `a`. * `a...` matches one or more occurrences of `a` * `[a]...` matches zero or more occurrences of `a`. * `(aaa | bbb || ccc)` shows `aaa` and `bbb` as candidates, and `ccc` only when current input matches neither `aaa` nor `bbb`. `||` behaves exactly like `|` when matching, it differs only when offering completions. Use parentheses to group patterns: * `a (b | c)` matches `a` followed by either `b` or `c`. * `(a | b) ...` matches `a` or `b` followed by any number of additional `a` or `b`. # `<_>` Nonterminal The special nonterminal `<_>` matches any shell word and doesn't produce the "undefined nonterminal" compilation warning so it's a good choice to use for program arguments where there isn't enough structure to produce meaningful completions but matching should still continue at the next shell word. ### Filename Completion There's a small set of predefined nonterminals that are handled specially by `complgen`: | Name | bash | fish | zsh | pwsh | Description | |---------------|------|------|-----|------|-------------| |`` | ✅ | ✅ | ✅ | ✅ | file or directory path | |`` | ✅ | ✅ | ✅ | ✅ | directory path | These can still be redefined in the grammar (` = ...`), in which case their predefined meaning gets overridden. ### Completion Descriptions (fish/zsh/pwsh) If a literal is immediately followed with a quoted string, it's going to appear as a hint to the user at completion time. E.g. the grammar: ```sh grep --extended-regexp "PATTERNS are extended regular expressions" | --exclude "skip files that match GLOB"; ``` results in something like this under fish, zsh, and PowerShell: ```fish fish> grep --ex --exclude (skip files that match GLOB) --extended-regexp (PATTERNS are extended regular expressions) ``` Note that `bash` does not support showing descriptions. In PowerShell, descriptions appear as tooltips. ### Sourcing Completions from External Commands It is possible to produce completions based on an external command output: cmd {{{ echo foo; echo bar; echo baz; echo quux }}}; ``` bash$ cmd bar baz foo quux ``` ##### Descriptions Externals commands are also assumed to produce descriptions similar to those described in the [section above](#descriptions). Their expected stdout format is a sequence of lines of the form ``` COMPLETION\tDESCRIPTION ``` For fish and zsh, the `DESCRIPTION` part will be presented to the user. Under bash, only the `COMPLETION` part will be visible. All external commands nonetheless need to take care as to *not* produce superfluous `\t` characters that may confuse the resulting shell scripts. ### Target Shell-specific Behavior External commands quickly degrade into necessitating shell-specific syntax. complgen provides support to conditionally choose the command based on the target shell. To use an example: all shells are able to complete users present on the system although each has a different function for it: ``` cmd ; = {{{ compgen -A user | sort | uniq }}}; # produce candidates on stdout under bash = {{{ __fish_complete_users }}}; # produce candidates on stdout under fish = {{{ _users }}}; # produce candidates via compadd and friends under zsh = {{{ Get-LocalUser | ForEach-Object { $_.Name } }}}; # PowerShell ``` complgen will pick the right definition of `` depending on what you're compiling the grammar to. ### Completion Within Words It's possible to match not only entire shell words, but also *within* words, using largely the same grammar syntax as for matching entire words, barring few spaces here and there. The most common application is to handle option arguments (e.g. `--option=ARGUMENT`): ``` grep --color=(always | never | auto); ``` The same mechanism works for more complicated things: ``` strace -e ; = [=][!][,]...; = trace | read | write | fault; = %file | file | all; ``` The above grammar was pulled straight out of [`strace` man page](https://man7.org/linux/man-pages/man1/strace.1.html#OPTIONS), illustrating how complgen follows the de facto standard man pages notation. ### Control What Gets Shown First If you do `git ` under the default completion that comes with git, you get something like: ``` bash$ git absorb bisect ci credential-gcloud fork help mv reflog revert show submodule whatchanged ad blame citool describe format-patch init notes refs reword show-branch sw worktree add br clang-format diff fsck instaweb prune remote rewrite sparse-checkout switch am branch clean difftool gc lfs pull repack rm stage tag amend bundle clone dlog gitk log push replace root stash touch apply checkout co fetch grep maintenance range-diff request-pull scalar stash-all tree archive cherry commit filter-repo gui merge rebase reset send-email stash-unstaged uncommit backfill cherry-pick config forgit head mergetool recent restore shortlog status wdiff ``` So even though git accepts many global options, they don't show up here! If OTOH you do `git --` instead: ``` bash$ git -- --bare --exec-path= --help --info-path --namespace= --no-replace-objects --version --exec-path --git-dir= --html-path --man-path --no-pager --paginate --work-tree= ``` You get presented with options in place of subcommands. It's a useful mechanism that allows to assigning levels of priority to possible choices, surfacing the most frequently used ones. Under complgen, the same effect is achieved via _fallbacks_ (`||`). An abridged version version of the above would look like below: ``` mygit ( ||