This document is intended to lower the bar for contributing to complgen
.
complgen
is architected somewhat like a compiler: it has several transformation stages and data flows only
in one direction like in a pipeline.
The stages are as follows:
- Parse a
.usage
file into a Rust data structure - Convert the data structure into a regular expression (in the textbook sense, not in the programmar sense)
- Convert the regular expression into a Deterministic Finite Automaton (DFA)
- For details, see Compilers Principles, Techniques and Tools (aka The Dragon Book), 2nd edition, section 3.9.5, "Converting a Regular Expression Directly to a DFA"
- Minimize the DFA
- For details, see Engineering a Compiler, 3rd edition, section "2.4.4 DFA to Minimal DFA"
- And also section 3.9.6, "Minimizing the Number of States of a DFA" from The Dragon Book.
- Emit a shell completion script
- Bash: Creating a bash completion script
- Fish: Writing your own completions — fish-shell documentation
- ZSH (zsh's completion system can be somewhat arcane in places):
complgen
is based on compleat, so it may also be useful sometimes to
reference it.
To ease understanding and debugging, some of the stages can generate visualizations (see output of complgen --help
for the relevant option name).
For the following grammar:
grep [<OPTION>]... <PATTERNS> [<FILE>]...;
<OPTION> ::= --extended-regexp "PATTERNS are extended regular expressions"
| --fixed-strings "PATTERNS are strings"
| --basic-regexp "PATTERNS are basic regular expressions"
| --perl-regexp "PATTERNS are Perl regular expressions"
| --regexp "use PATTERNS for matching" <PATTERNS>
| --file "take PATTERNS from FILE" <FILE>
| --ignore-case "ignore case distinctions in patterns and data"
| --no-ignore-case "do not ignore case distinctions (default)"
| --word-regexp "match only whole words"
| --line-regexp "match only whole lines"
| --null-data "a data line ends in 0 byte, not newline"
| --no-messages "suppress error messages"
| --invert-match "select non-matching lines"
| --version "display version information and exit"
| --help "display this help text and exit"
| --max-count "stop after NUM selected lines" <NUM>
| --byte-offset "print the byte offset with output lines"
| --line-number "print line number with output lines"
| --line-buffered "flush output on every line"
| --with-filename "print file name with output lines"
| --no-filename "suppress the file name prefix on output"
| --label "use LABEL as the standard input file name prefix" <LABEL>
| --only-matching "show only nonempty parts of lines that match"
| --quiet "suppress all normal output"
| --silent "suppress all normal output"
| --binary-files "assume that binary files are TYPE; TYPE is 'binary', 'text', or 'without-match'" <TYPE>
| --text "equivalent to --binary-files=text"
| --directories "how to handle directories;" <ACTION-DIRECTORIES>
| --devices "how to handle devices, FIFOs and sockets;" <ACTION-DEVICES>
| --recursive "like --directories=recurse"
| --dereference-recursive "likewise, but follow all symlinks"
| --include "search only files that match GLOB (a file pattern)" <GLOB>
| --exclude "skip files that match GLOB" <GLOB>
| --exclude-from "skip files that match any file pattern from FILE" <FILE>
| --exclude-dir "skip directories that match GLOB" <GLOB>
| --files-without-match "print only names of FILEs with no selected lines"
| --files-with-matches "print only names of FILEs with selected lines"
| --count "print only a count of selected lines per FILE"
| --initial-tab "make tabs line up (if needed)"
| --null "print 0 byte after FILE name"
| --before-context "print NUM lines of leading context" <NUM>
| --after-context "print NUM lines of trailing context" <NUM>
| --context NUM "print NUM lines of output context" <NUM>
| --group-separator "print SEP on line between matches with context" <SEP>
| --no-group-separator "do not print separator for matches with context"
| --color "use markers to highlight the matching strings" [<WHEN>]
| --colour "WHEN is 'always', 'never', or 'auto'" [<WHEN>]
| --binary "do not strip CR characters at EOL (MSDOS/Windows)"
;
<ACTION-DIRECTORIES> ::= read | recurse | skip;
<ACTION-DEVICES> ::= read | skip;
<TYPE> ::= binary | text | without-match;
<WHEN> ::= always | never | auto;
<FILE> ::= { ls };
its abstract syntax tree can be visualized as a railroad diagram:
The DFA can be visualized as a Graphviz diagram:
- clap
complgen
is able to produce completions by executing an arbitrary shell command (e.g.cargo -Z <TAB>
, complete test name incargo test <TAB>
)- All the grammar specification mechanisms are available for completing option parameters. That means
complgen
is able to complete DSLs of the likes ofstrace -e <TAB>
orlsof -i <TAB>
. - No recompilation and reloading necessary in shell integration mode -- just modify the grammar file and completions automatically reflect that.
- There's a possibility of the program options and completion grammar diverging since they're maintained
separately. On the plus side,
complgen
isn't tied to the implementation language and independent users can write their custom completion grammars suited for their own needs.
- zsh-capture-completion
- This must have been painful to implement but is indispensable to complgen!
- argcomplete
- Oil's shellac protocol
- _regex_arguments and _regex_words completions
- sh-manpage-completions
- Little Language