diff --git a/doc/reference/std/cli/getopt.md b/doc/reference/std/cli/getopt.md index d4930f79a..b959fd77a 100644 --- a/doc/reference/std/cli/getopt.md +++ b/doc/reference/std/cli/getopt.md @@ -249,3 +249,22 @@ e.g. with prototype objects like `gerbil-poo`. TODO: add examples, discuss abort-on-error behavior, lack of automatic help, etc. + +### ->getopt-spec +```scheme +(->getopt-spec arg) => list-of-getopt-arguments +``` +Given an argument `arg`, return a list *lst* of getopt arguments +to which one can `(apply getopt lst)` to specify a getopt object to parse with. + +Default behavior: + - If `arg` is a list, + [`flatten`](../misc/list.md#flatten) it. + - If `arg` is a natural integer *n*, + specify a list of *n* positional `argument`s. + - If `arg` is `#f`, specify a single `rest-argument` named `rest`, + i.e. let it be a passthrough to be processed by the function being called. + - Otherwise, raise an error. + +This function is useful for calls not just to `getopt` directly, +but also to `command` that itself calls `getopt`, etc. diff --git a/doc/reference/std/cli/multicall.md b/doc/reference/std/cli/multicall.md index 47877e66c..e23327040 100644 --- a/doc/reference/std/cli/multicall.md +++ b/doc/reference/std/cli/multicall.md @@ -14,17 +14,114 @@ in gerbil-utils. ## Interface ### current-program -### entry-points +```scheme +(def current-program (make-parameter [])) +``` +A parameter that contains the name of the current program or subprogram, +as a list in reverse of the successive subcommands used to invoke it. + ### current-program-string +```scheme +(def current-program (make-parameter [])) +``` +Return as a string of space-separated commands and subcommands in order +the name of the current program or subprogram. + +### entry-points +```scheme +entry-points => table +``` +A table, indexed by symbols, of `entry-point` structs, +describing the available shell entry points. + ### entry-point -### getopt-spec +```scheme +(defstruct entry-point (name function help getopt) transparent: #t) +``` +A struct type describing an available entry-point: + - `name` is a symbol, whose `symbol->string` is used as command or subcommand + to select the entry-point from the CLI. + - `function` is the Scheme function to call if the entry-point is selected. + - `help` is a short help string describing the purpose of the entry-point, + to be displayed to the user when help is requested. + - `getopt` is a `getopt-spec` + based on which the rest of the command-line will be parsed, and + based on which help about the available options is displayed. + ### entry-points-getopt-spec +```scheme +(entry-points-getopt-spec [table]) +``` +Given a `table` of entry-points which default to the variable `entry-points`, +return a getopt-spec (suitable to be passed to `(apply getopt ...)`) of +`command` specifiers, one for each registered entry-point, in asciibetical order. + ### register-entry-point +```scheme +(register-entry-point function + [id: #f] [name: #f] [help: #f] [getopt: #f]) +``` +Register the function as entry-point, +with given `name` (argument passed to `make-symbol`), +or if not specified, a symbol made of only the +[easy-shell-characters](shell.md#easy-shell-characters?) of `id`. +The entry-point will have the given `help` and `getopt` fields. + ### define-entry-point +```scheme +(define-entry-point (id . formals) (options ...) body ...) +``` +Syntax that expands to both + 1. defining in the current scope function with the given name `id` + and specified Scheme function formals, and the given `body`. + 2. register an entry-point for that function, + with given `id` and `options`. + ### multicall-default +```scheme +multicall-default +``` +A mutable variable that contains the default function to call +if the command doesn’t match any of the specified commands. + ### set-default-entry-point! +```scheme +(set-default-entry-point! symbol) +``` +Set the default entry-point in `multicall-default` as given `symbol`. + ### help +```scheme +(help [command]) +``` +Global entry-point to print a help message (about the command, if specified) +about the current overall command and subcommands. + ### meta +```scheme +(meta) +``` +Global entry-point to print the available completions for the command, +for use with CLI syntax autodetection. + ### version +```scheme +(version [all?: #f] [layer]) +``` +Global entry-point to print the current version. +If `all?` (flag `-a`) is passed, print all components from build manifest. +If `layer` (flag `-l`) is passed, print the thus-named component. + ### call-entry-point +```scheme +(call-entry-point . args) +``` +Call an entry point as specified by `args`, +or else the `multicall-default` entry point. + ### define-multicall-main +```scheme +define-multicall-main +``` +Define `call-entry-point` as a suitable `main` function +in the current scope. diff --git a/src/std/cli/getopt.ss b/src/std/cli/getopt.ss index a646bd9d8..16b9f3cbf 100644 --- a/src/std/cli/getopt.ss +++ b/src/std/cli/getopt.ss @@ -4,8 +4,9 @@ (import (only-in :std/error deferror-class Error:::init!) (only-in :std/generic defgeneric) + (only-in :std/iter for/collect in-iota) (only-in :std/misc/hash hash->list/sort) - (only-in :std/misc/list when/list) + (only-in :std/misc/list when/list flatten) (only-in :std/misc/string as-stringgetopt-spec + (lambda (spec) + (cond + ((list? spec) (flatten spec)) + ((nat? spec) (for/collect ((i (in-iota spec 1))) (argument (format "arg~d" i)))) + ((not spec) (rest-arguments "rest")) + (else (error "Bad getopt spec"))))) diff --git a/src/std/cli/multicall.ss b/src/std/cli/multicall.ss index 41272958b..bd724469e 100644 --- a/src/std/cli/multicall.ss +++ b/src/std/cli/multicall.ss @@ -11,7 +11,6 @@ (only-in :std/getopt getopt getopt-display-help-topic getopt-display-help call-with-processed-command-line command flag option argument optional-argument rest-arguments) - (only-in :std/iter for/collect in-iota) (only-in :std/misc/hash hash->list/sort) (only-in :std/misc/list flatten) (only-in :std/misc/number nat?) @@ -28,18 +27,10 @@ (defstruct entry-point (name function help getopt) transparent: #t) -(defgeneric getopt-spec - (lambda (spec) - (cond - ((list? spec) spec) - ((nat? spec) (for/collect ((i (in-iota spec 1))) (argument (number->string i)))) - ((not spec) [(rest-arguments "rest")]) - (else (error "Bad getopt-spec"))))) - (def (entry-points-getopt-spec (h entry-points)) (for/collect (([name . e] (hash->list/sort h as-stringgetopt-spec (entry-point-getopt e))))) ;; TODO: allow registering a getopt: structure and/or other command information, ;; so we can show detailed help and automatically parse arguments? @@ -65,12 +56,13 @@ (help: "Print help about available commands" getopt: [(optional-argument 'command help: "subcommand for which to display help")]) (displayln (display-build-manifest (filter-build-manifest))) - (def gopt (apply getopt (entry-points-getopt-spec))) + (def gopt (getopt (entry-points-getopt-spec))) (def program (current-program-string (cdr (current-program)))) (if command (getopt-display-help-topic gopt (make-symbol command) program) (getopt-display-help gopt program))) +;; TODO: also handle getopt specifications? (define-entry-point (meta) (help: "Print meta-information for completion purposes" getopt: [])