object_rework: working MVP with runtime CommandNode tree
- BaseCommandNode (abstract) / CommandNode(T) split for heterogeneous tree - Flag(T) with compile-time type branching, Parsable/Coercable modules - Arg double-process invariant enforcement - App.generate macro builds CommandNode tree from Command subclasses - Command#initialize generated via macro finished, populates ivars from handler - Reserved -h/--help enforcement in Flag#check! - MatchType::Help added, SubCommand dispatch fixed - design.adoc: added Planned Features (markdown docs, bash completion) - Makefile: tabs, .DEFAULT_GOAL, doc_show target Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+67
@@ -74,3 +74,70 @@ The examples like above provide a "DSL-esk" way of defining:
|
||||
==== How it works
|
||||
|
||||
Using crystal macros, you define the shape (arguments/flags, selections, work functions/subcommands, etc
|
||||
|
||||
== Planned Features
|
||||
|
||||
=== Markdown Documentation Generation
|
||||
|
||||
Since all command metadata is present in annotations at compile time (`@[CliGen::CommandInfo]`, `@[CliGen::SubCommand]`, `@[CliGen::Argument]`, `@[CliGen::Selection]`), the framework can walk the same structures that `generate.cr` already walks and render them into a Markdown document instead of a `CommandNode` tree.
|
||||
|
||||
The generation would be driven by a `macro finished` block (similar to `generate.cr`) that emits a `self.generate_docs` class method on `App`. This method walks every `Command` subclass and its annotations to produce a structured document.
|
||||
|
||||
Proposed output structure:
|
||||
|
||||
----
|
||||
# <AppName>
|
||||
|
||||
## Commands
|
||||
|
||||
### mycommand
|
||||
<CommandInfo description>
|
||||
|
||||
#### Flags
|
||||
| Flag | Short | Type | Default | Description |
|
||||
|------|-------|------|---------|-------------|
|
||||
| --myvar VAR | -m | Int32 | 23 | ... |
|
||||
|
||||
#### Subcommands
|
||||
- `do_thing` — <description>
|
||||
- Examples: ...
|
||||
----
|
||||
|
||||
Implementation notes:
|
||||
|
||||
* Driven by a `--generate-docs` global flag or a dedicated class method
|
||||
* ECR templates (already pulled in) are the natural rendering mechanism
|
||||
* The same annotation data powers both runtime help output and the doc generator, keeping them in sync automatically
|
||||
|
||||
=== Bash Autocompletion Generation
|
||||
|
||||
Since all command names and flag names are known at compile time, a complete bash completion script can be generated statically — no runtime `--completions` endpoint needed.
|
||||
|
||||
The approach is a hidden `--generate-completion bash` flag (potentially extended to `zsh`/`fish` later) that prints a ready-to-install completion script to stdout.
|
||||
|
||||
Proposed completion script shape:
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
_myapp() {
|
||||
local cur="${COMP_WORDS[COMP_CWORD]}"
|
||||
local prev="${COMP_WORDS[COMP_CWORD-1]}"
|
||||
|
||||
case "${COMP_WORDS[1]}" in
|
||||
mycommand)
|
||||
COMPREPLY=($(compgen -W "--myvar -m --format -f" -- "$cur"))
|
||||
;;
|
||||
*)
|
||||
COMPREPLY=($(compgen -W "mycommand myothercommand" -- "$cur"))
|
||||
;;
|
||||
esac
|
||||
}
|
||||
complete -F _myapp myapp
|
||||
----
|
||||
|
||||
Implementation notes:
|
||||
|
||||
* Script body generated at compile time via a `macro finished` walk of `Command.subclasses`
|
||||
* `@[CliGen::Selection]` options (`%w[json yaml ecr]`) can be included as valid completions for their flag
|
||||
* Install path: `myapp --generate-completion bash > ~/.bash_completion.d/myapp` or printed with instructions
|
||||
* Same annotation data used by the doc generator, so both stay in sync with the command definition
|
||||
|
||||
Reference in New Issue
Block a user