# HG changeset patch # User Steve Losh # Date 1554242283 14400 # Node ID 860e105446521eaa92fec39dd2e38708db49c47c # Parent cc1aad82d9f6b8466057322fb7ab76d703085081 Start the actual documentation diff -r cc1aad82d9f6 -r 860e10544652 LICENSE.markdown --- a/LICENSE.markdown Tue Apr 02 09:51:25 2019 -0400 +++ b/LICENSE.markdown Tue Apr 02 17:58:03 2019 -0400 @@ -1,4 +1,4 @@ -Copyright (c) 2018 Steve Losh and contributors +Copyright (c) 2019 Steve Losh and contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff -r cc1aad82d9f6 -r 860e10544652 README.markdown --- a/README.markdown Tue Apr 02 09:51:25 2019 -0400 +++ b/README.markdown Tue Apr 02 17:58:03 2019 -0400 @@ -1,10 +1,11 @@ Adopt ===== -I needed **A** **D**amn **OPT**ion parsing library. +I needed **a** **d**amn **opt**ion parsing library. -Adopt is a simple UNIX-style option parser in Common Lisp. It depends on -[Bobbin][] and [split-sequence][]. +Adopt is a simple UNIX-style option parser in Common Lisp, heavily influenced by +Python's optparse and argparse. It depends on [Bobbin][] and +[split-sequence][]. * **License:** MIT * **Documentation:** diff -r cc1aad82d9f6 -r 860e10544652 docs/01-installation.markdown --- a/docs/01-installation.markdown Tue Apr 02 09:51:25 2019 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,17 +0,0 @@ -Installation -============ - -Adopt is compatible with Quicklisp, but not *in* Quicklisp (yet?). You can -clone the repository into your [Quicklisp local-projects directory][local] for -now. - -The `adopt` system contains the core API and depends on [Bobbin][] and -[split-sequence][]. - -The `adopt.test` system contains the test suite, which uses depends on some -other systems. You don't need to load this unless you want to run the unit -tests. - -[local]: https://www.quicklisp.org/beta/faq.html#local-project -[Bobbin]: https://github.com/sjl/bobbin -[split-sequence]: https://www.cliki.net/split-sequence diff -r cc1aad82d9f6 -r 860e10544652 docs/01-usage.markdown --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/01-usage.markdown Tue Apr 02 17:58:03 2019 -0400 @@ -0,0 +1,315 @@ +Usage +===== + +Adopt is a simple library for parsing UNIX-style command line arguments in +Common Lisp. It was made because none of the other libraries did what I needed. + +[TOC] + +Package +------- + +All core Adopt functions are in the `adopt` package. Several of the symbols in +adopt shadow those in the `common-lisp` package, so you should probably use +namespaced `adopt:…` symbols instead of `USE`ing the package. + +Interfaces +---------- + +To get started with Adopt, you should create an interface with the +`adopt:make-interface` function. This returns an object representing the +command line interface presented to your users. + +### Creating an Interface + +Let's say you're developing a program to search the contents of files (because +the world certainly needs *another* `grep` replacement). You might start with +something like: + + (defparameter *ui* + (adopt:make-interface + :name "search" + :summary "search files for a regular expression" + :usage "[OPTIONS] PATTERN [FILE]..." + :help "Search the contents of each FILE for the regular expression PATTERN. If no files are specified, searches standard input instead.")) + +You can now print some help text for your CLI with `adopt:print-help`: + + (adopt:print-help *ui*) + ; => + search - search files for a regular expression + + USAGE: … [OPTIONS] PATTERN [FILE]... + + Search the contents of each FILE for the regular expression PATTERN. If no + files are specified, searches standard input instead. + +### Line Wrapping + +Adopt will handle line-wrapping your help text, so you don't need to (and +shouldn't) add extra line breaks when creating your interface. If you want to +line break the text in your source code to fit nicely in your editor, remember +that `adopt:make-interface` is just a function — you can use `format` (possibly +with its `~Newline` directive) to preprocess the help text argument: + + (defparameter *ui* + (adopt:make-interface + :name "search" + :summary "search files for a regular expression" + :usage "[OPTIONS] PATTERN [FILE]..." + :help (format nil "Search the contents of each FILE for ~ + the regular expression PATTERN. If ~ + no files are specified, searches ~ + standard input instead."))) + +Adopt's line-wrapping library [Bobbin][] will only ever *add* line breaks, never +remove them, which means you can include breaks in the output if you want to +have multiple paragraphs in your help text: + + (defparameter *ui* + (adopt:make-interface + :name "search" + :summary "search files for a regular expression" + :usage "[OPTIONS] PATTERN [FILE]..." + :help (format nil + "Search the contents of each FILE for the regular expression PATTERN.~@ + ~@ + If no files are specified (or if - is given as a file name) standard input will be searched instead."))) + +If you want to control the width of the help text lines when they are printed, +`adopt:print-help` takes a `:width` argument: + + (adopt:print-help *ui* :width 50) + ; => + search - search files for a regular expression + + USAGE: … [OPTIONS] PATTERN [FILE]... + + Search the contents of each FILE for the regular + expression PATTERN. + + If no files are specified (or if - is given as a + file name) standard input will be searched + instead. + +`adopt:print-help` takes a number of other options — see the API Reference for +more information. + +### Examples + +Describing the CLI in detail is helpful, but users can often learn a lot more by +seeing a few examples of its usage. `adopt:make-interface` can take an +`:examples` argument, which should be an alist of `(description . example)` +conses: + + (defparameter *ui* + (adopt:make-interface + :name "search" + :summary "search files for a regular expression" + :usage "[OPTIONS] PATTERN [FILE]..." + :help … + :examples + '(("Search foo.txt for the string 'hello':" + . "search hello foo.txt") + ("Search standard input for lines starting with x:" + . "search '^x' -") + ("Watch the file log.txt for lines containing the username steve.losh:" + . "tail foo/bar/baz/log.txt | search --literal steve.losh -")))) + + (adopt:print-help *ui* :width 50) + ; => + search - search files for a regular expression + + USAGE: … [OPTIONS] PATTERN [FILE]... + + Search the contents of each FILE for the regular + expression PATTERN. + + If no files are specified (or if - is given as a + file name) standard input will be searched + instead. + + Examples: + + Search foo.txt for the string 'hello': + + search hello foo.txt + + Search standard input for lines starting with x: + + search '^x' - + + Watch the file log.txt for lines containing the + username steve.losh: + + tail foo/bar/baz/log.txt | search --literal steve.losh - + +Notice how Adopt line wraps the prose explaining each example, but leaves the +example itself untouched for easier copying and pasting. In general Adopt tries +to do the right thing for your users (even when that means making a little more +work for *you* in certain places). + +Exiting +------- + +Adopt provides some helpful utility functions to exit out of your program with +a UNIX exit code. These do what you think they do: + + (adopt:exit) + + (adopt:exit 1) + + (adopt:print-help-and-exit *ui*) + + (adopt:print-help-and-exit *ui* + :stream *error-output* + :exit-code 1) + + (handler-case (assert (= 1 0)) + (error (err) + (adopt:print-error-and-exit err))) + +These functions are not implemented for every Lisp implementation. PRs are +welcome, or you can just write the implementation-specific calls in your program +yourself. + +Options +------- + +Now that you know how to create an interface, you can create some options to use +inside it with `adopt:make-option`: + + (defparameter *option-version* + (adopt:make-option 'version + :long "version" + :help "display version information and exit" + :reduce (constantly t))) + + (defparameter *option-help* + (adopt:make-option 'help + :long "help" + :short #\h + :help "display help information and exit" + :reduce (constantly t))) + + (defparameter *option-literal* + (adopt:make-option 'literal + :long "literal" + :short #\l + :help "treat PATTERN as a literal string instead of a regular expression" + :reduce (constantly t))) + + (defparameter *ui* + (adopt:make-interface + :name "search" + :summary "search files for a regular expression" + :usage "[OPTIONS] PATTERN [FILE]..." + :help "Search the contents of …" + :contents (list + *option-version* + *option-help* + *option-literal*))) + +Adopt will automatically add the options to the help text: + + (adopt:print-help *ui*) + ; => + search - search files for a regular expression + + USAGE: /usr/local/bin/sbcl [OPTIONS] PATTERN [FILE]... + + Search the contents of … + + Options: + --version display version information and exit + -h, --help display help information and exit + -l, --literal treat PATTERN as a literal string instead of a regular + expression + +The first argument to `adopt:make-option` is the name of the option, which we'll +see put to use shortly. At least one of `:short` and `:long` is required, and +`:help` text must be specified. We'll talk more about `:reduce` in a bit, but +it too is required. + +I prefer to define each option as its own global variable to keep the call to +`adopt:make-interface` from getting too large and unwieldy, but feel free to do +something like this if you prefer to avoid cluttering your package: + + (defparameter *ui* + (adopt:make-interface + … + :contents + (list (adopt:make-option 'foo …) + (adopt:make-option 'bar …) + …))) + +Parsing +------- + +At this point we've got an interface with some options, so we can use it to +parse a list of strings we've received as command line arguments: + + (adopt:parse-options *ui* '("foo.*" "--literal" "a.txt" "b.txt")) + ; => + ("foo.*" "a.txt" "b.txt") + # + +`adopt:parse-options` returns two values: a list of non-option arguments, and +a hash table of the option values. + +The keys of the hash table are (by default) the option names given as the first +argument to `adopt:make-option`. We'll see how the option values are determined +soon. + +From now on I'll use a special pretty printer for hash tables to make it easier +to see what's inside them: + + (adopt:parse-options *ui* '("foo.*" "--literal" "a.txt" "b.txt")) + ; => + ("foo.*" "a.txt" "b.txt") + {LITERAL: T, VERSION: NIL, HELP: NIL} + +Top-Level Structure +------------------- + +We'll look at how the option values are computed shortly, but first let's see +the overall structure of the programs you create with Adopt: + + (defun toplevel () + (handler-case + (multiple-value-bind (arguments options) + (adopt:parse-options *ui*) + (when (gethash 'help options) + (adopt:print-help-and-exit *ui*)) + (when (gethash 'version options) + (format t "1.0.0~%") + (adopt:exit)) + (destructuring-bind (pattern . files) arguments + (run pattern + files + :literal (gethash 'literal options)))) + (error (c) + (adopt:print-error-and-exit c)))) + + (sb-ext:save-lisp-and-die "search" :toplevel #'toplevel) + +The `toplevel` function first uses a `handler-case` to trap all `error`s. If +any error occurs it will print the error message and exit, to avoid confusing +users by dropping them into a Lisp debugger REPL (which they probably won't +understand). When you're developing your program yourself you'll want to omit +this part and let yourself land in the debugger as usual. + +Next we use `adopt:parse-options` to parse the command line arguments and +options. We do some initial checks to see if the user wants `--help` or +`--version` information. If not, we destructure the arguments into the items we +expect and call a `run` function with all the information it needs to do its +job. + +If the `destructuring-bind` fails an error will be signaled, and the +`handler-case` will print it and exit. If you want to be a nice person you +could check that the `arguments` have the correct shape first, and return +a friendlier error message to your users if they don't. + +Computing Values with Reduce +---------------------------- + diff -r cc1aad82d9f6 -r 860e10544652 docs/02-reference.markdown --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/02-reference.markdown Tue Apr 02 17:58:03 2019 -0400 @@ -0,0 +1,247 @@ +# API Reference + +The following is a list of all user-facing parts of adopt. + +If there are backwards-incompatible changes to anything listed here, they will +be noted in the changelog and the author will feel bad. + +Anything not listed here is subject to change at any time with no warning, so +don't touch it. + +[TOC] + +## Package `ADOPT` + +### `ARGV` (function) + + (ARGV) + +Return a list of the program name and command line arguments. + + This is not implemented for every Common Lisp implementation. You can always + pass your own values to `parse-options` and `print-help` if it's not + implemented for your particular Lisp. + + + +### `COLLECT` (function) + + (COLLECT LIST EL) + +Append element `el` to the end of `list`. + + It is useful as a `:reduce` function when you want to collect all values given + for an option. + + This is implemented as `(append list (list el))`. It is not particularly + fast. If you can live with reversed output consider `(flip #'cons)` instead. + + + +### `DEFINE-STRING` (macro) + + (DEFINE-STRING VAR STRING &REST ARGS) + +Convenience macro for `(defparameter ,var (format nil ,string ,@args))`. + +### `DISCARD-OPTION` (function) + + (DISCARD-OPTION CONDITION) + +Invoke the `discard-option` restart properly. + + Example: + + (handler-bind ((unrecognized-option 'discard-option)) + (multiple-value-bind (arguments options) (parse-options *ui*) + (run arguments options))) + + + +### `EXIT` (function) + + (EXIT &OPTIONAL (CODE 0)) + +### `FIRST` (function) + + (FIRST OLD NEW) + +Return `new` if `old` is `nil`, otherwise return `old`. + + It is useful as a `:reduce` function when you want to just keep the + first-given value for an option. + + + +### `FLIP` (function) + + (FLIP FUNCTION) + +Return a function of two arguments X and Y that calls `function` with Y and X. + + Useful for wrapping existing functions that expect their arguments in the + opposite order. + + Examples: + + (funcall #'cons 1 2) ; => (1 . 2) + (funcall (flip #'cons) 1 2) ; => (2 . 1) + (reduce (flip #'cons) '(1 2 3) :initial-value nil) + ; => (3 2 1) + + + +### `LAST` (function) + + (LAST OLD NEW) + +Return `new`. + + It is useful as a `:reduce` function when you want to just keep the last-given + value for an option. + + + +### `MAKE-GROUP` (function) + + (MAKE-GROUP NAME &KEY TITLE HELP MANUAL OPTIONS) + +### `MAKE-INTERFACE` (function) + + (MAKE-INTERFACE &KEY NAME SUMMARY USAGE HELP MANUAL EXAMPLES CONTENTS) + +### `MAKE-OPTION` (function) + + (MAKE-OPTION NAME &KEY LONG SHORT HELP MANUAL PARAMETER REDUCE (RESULT-KEY NAME) + (INITIAL-VALUE NIL INITIAL-VALUE?) (KEY #'IDENTITY) (FINALLY #'IDENTITY)) + +### `PARSE-OPTIONS` (function) + + (PARSE-OPTIONS INTERFACE &OPTIONAL (ARGUMENTS (REST (ARGV)))) + +Parse `arguments` according to `interface`. + + Two values are returned: + + 1. A fresh list of top-level, unaccounted-for arguments that don't correspond + to any options defined in `interface`. + 2. An `EQL` hash table of option keys to values. + + See the full documentation for more information. + + + +### `PRINT-ERROR-AND-EXIT` (function) + + (PRINT-ERROR-AND-EXIT ERROR &KEY (STREAM *ERROR-OUTPUT*) (EXIT-CODE 1) (PREFIX error: )) + +Print `prefix` and `error` to `stream` and exit. + + Example: + + (handler-case + (multiple-value-bind (arguments options) (parse-options *ui*) + (run arguments options)) + (unrecognized-option (c) + (print-error-and-exit c))) + + + +### `PRINT-HELP` (function) + + (PRINT-HELP INTERFACE &KEY (STREAM *STANDARD-OUTPUT*) (PROGRAM-NAME (CAR (ARGV))) (WIDTH 80) + (OPTION-WIDTH 20) (INCLUDE-EXAMPLES T)) + +Print a pretty help document for `interface` to `stream`. + + `width` should be the total width (in characters) for line-wrapping purposes. + Care will be taken to ensure lines are no longer than this, though some edge + cases (extremely long short/long option names and parameters) may slip + through. + + `option-width` should be the width of the column of short/long options (in + characters). If the short/long option help is shorter than this, the option's + help string will start on the same line. Otherwise the option's help string + will start on the next line. + + The result will look something like: + + (print-help *program-interface* :width 60 :option-width 15) + ; => + ; foo - do some things and meow + ; + ; USAGE: /bin/foo [options] FILES + ; + ; Foo is a program to frobulate some files, meowing as it + ; happens. + ; + ; Options: + ; -v, --verbose Output extra information. + ; -q, --quiet Shut up. + ; --ignore FILE Ignore FILE. May be specified multiple + ; times. + ; -n NAME, --name NAME + ; Your name. May be specified many times, + ; last one wins. + ; -m, --meow Meow. + ; 0.........1.... option-width + ; 0........1.........2.........3.........4.........5.........6 + + + +### `PRINT-HELP-AND-EXIT` (function) + + (PRINT-HELP-AND-EXIT INTERFACE &KEY (STREAM *STANDARD-OUTPUT*) (PROGRAM-NAME (CAR (ARGV))) + (WIDTH 80) (OPTION-WIDTH 20) (INCLUDE-EXAMPLES T) (EXIT-CODE 0)) + +Print a pretty help document for `interface` to `stream` and exit. + + Handy for easily providing --help: + + (multiple-value-bind (arguments options) (parse-options *ui*) + (when (gethash 'help options) + (print-help-and-exit *ui*)) + (run arguments options)) + + +### `PRINT-MANUAL` (function) + + (PRINT-MANUAL INTERFACE &KEY (STREAM *STANDARD-OUTPUT*) (MANUAL-SECTION 1)) + +### `SUPPLY-NEW-VALUE` (function) + + (SUPPLY-NEW-VALUE CONDITION VALUE) + +Invoke the `supply-new-value` restart properly. + + Example: + + (handler-bind + ((unrecognized-option (alexandria:rcurry 'supply-new-value "--foo")) + (multiple-value-bind (arguments options) (parse-options *ui*) + (run arguments options))) + + + +### `TREAT-AS-ARGUMENT` (function) + + (TREAT-AS-ARGUMENT CONDITION) + +Invoke the `treat-as-argument` restart properly. + + Example: + + (handler-bind ((unrecognized-option 'treat-as-argument)) + (multiple-value-bind (arguments options) (parse-options *ui*) + (run arguments options))) + + + +### `UNRECOGNIZED-OPTION` (class) + +#### Slot `PROBLEMATIC-OPTION` + +* Allocation: `:INSTANCE` +* Initarg: `:PROBLEMATIC-OPTION` +* Accessor: `PROBLEMATIC-OPTION` + diff -r cc1aad82d9f6 -r 860e10544652 docs/02-usage.markdown --- a/docs/02-usage.markdown Tue Apr 02 09:51:25 2019 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,65 +0,0 @@ -Usage -===== - -Adopt is a simple library for parsing UNIX-style command line arguments in -Common Lisp. It was made because none of the other libraries did what I needed. - -[TOC] - -Package -------- - -All core Adopt functions and macros are in the `adopt` package. You can `:use` -that if you really want to, but it's probably clearer to use namespaced -`adopt:…` symbols. - -Interfaces ----------- - -define-interface and usage. nothing else. - -Parsing -------- - -`parse-options` with a bare-bones interface. - -Options -------- - -talk about the option arguments. - -Short and Long Options ----------------------- - -short versus long arguments. 1+ is required. - -Initial Value -------------- - -at this point it's just a default - -Reduce ------- - -talk about reducers. mention argparse's: - -`store_const` is `(constantly x)` - -`store` is `#'latest` - -`store_true` is `(constantly t)` - -`store_false` is `(constantly nil)` - -`append` is `append1` - -`append_const` is `(rcurry #'append1 x)` - -`count` is `1+` with an `initial-value` of `0`. - -need to figure out `help` and `version`. - -Usage Printing --------------- - -It's `(print-usage interface)`. diff -r cc1aad82d9f6 -r 860e10544652 docs/03-changelog.markdown --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/03-changelog.markdown Tue Apr 02 17:58:03 2019 -0400 @@ -0,0 +1,11 @@ +Changelog +========= + +Here's the list of changes in each released version. + +[TOC] + +Current Development Version +--------------------------- + +Adopt is still in active development. No promises yet. diff -r cc1aad82d9f6 -r 860e10544652 docs/03-reference.markdown --- a/docs/03-reference.markdown Tue Apr 02 09:51:25 2019 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,108 +0,0 @@ -# API Reference - -The following is a list of all user-facing parts of adopt. - -If there are backwards-incompatible changes to anything listed here, they will -be noted in the changelog and the author will feel bad. - -Anything not listed here is subject to change at any time with no warning, so -don't touch it. - -[TOC] - -## Package `ADOPT` - -### `APPEND1` (function) - - (APPEND1 LIST EL) - -Append element `el` to the end of `list`. - - This is implemented as `(append list (list el))`. It is not particularly - fast. - - It is useful as a `:reduce` function when you want to collect all values given - for an option. - - - -### `ARGV` (function) - - (ARGV) - -Return a list of the program name and command line arguments. - - This is not implemented for every Common Lisp implementation. You can always - pass your own values to `parse-options` and `print-usage` if it's not - implemented for your particular Lisp. - - - -### `DEFINE-INTERFACE` (macro) - - (DEFINE-INTERFACE NAME USAGE &REST OPTIONS) - -### `LATEST` (function) - - (LATEST OLD NEW) - -Return `new`. - - It is useful as a `:reduce` function when you want to just keep the last-given - value for an option. - - - -### `PARSE-OPTIONS` (function) - - (PARSE-OPTIONS INTERFACE &OPTIONAL (ARGUMENTS (REST (ARGV)))) - -Parse `arguments` according to `interface`. - - Two values are returned: - - 1. A fresh list of top-level, unaccounted-for arguments that don't correspond - to any options defined in `interface`. - 2. An `EQL` hash map of option `name`s to values. - - See the full usage documentation for more information. - - - -### `PRINT-USAGE` (function) - - (PRINT-USAGE INTERFACE &KEY (STREAM *STANDARD-OUTPUT*) (PROGRAM-NAME (FIRST (ARGV))) (WIDTH 80) (OPTION-WIDTH 20)) - -Print a pretty usage document for `interface` to `stream`. - - `width` should be the total width (in characters) for line-wrapping purposes. - Care will be taken to ensure lines are no longer than this, though some edge - cases (extremely long short/long option names and parameters) may slip - through. - - `option-width` should be the width of the column of short/long options (in - characters). If the short/long option documentation is shorter than this, the - option's documentation string will start on the same line. Otherwise the - option's documentation string will start on the next line. - - The result will look something like (assuming a usage string of - `"[options] FILES"`): - - (print-usage *program-interface* :width 60 :option-width 15) - ; => - ; USAGE: /bin/foo [options] FILES - ; - ; Options: - ; -v, --verbose Output extra information. - ; -q, --quiet Shut up. - ; --ignore FILE Ignore FILE. May be specified multiple - ; times. - ; -n NAME, --name NAME - ; Your name. May be specified many times, - ; last one wins. - ; -m, --meow Meow. - ; 0.........1.... option-width - ; 0........1.........2.........3.........4.........5.........6 - - - diff -r cc1aad82d9f6 -r 860e10544652 docs/04-changelog.markdown --- a/docs/04-changelog.markdown Tue Apr 02 09:51:25 2019 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,11 +0,0 @@ -Changelog -========= - -Here's the list of changes in each released version. - -[TOC] - -Current Development Version ---------------------------- - -Adopt is still in active development. No promises yet. diff -r cc1aad82d9f6 -r 860e10544652 docs/api.lisp --- a/docs/api.lisp Tue Apr 02 09:51:25 2019 -0400 +++ b/docs/api.lisp Tue Apr 02 17:58:03 2019 -0400 @@ -13,7 +13,7 @@ (d-api:generate-documentation :adopt - #p"docs/03-reference.markdown" + #p"docs/02-reference.markdown" (list "ADOPT") *header* :title "API Reference") diff -r cc1aad82d9f6 -r 860e10544652 docs/index.markdown --- a/docs/index.markdown Tue Apr 02 09:51:25 2019 -0400 +++ b/docs/index.markdown Tue Apr 02 17:58:03 2019 -0400 @@ -1,17 +1,28 @@ -I needed **A** **D**amn **OPT**ion parsing library. +I needed **a** **d**amn **opt**ion parsing library. -Adopt is a simple (~150 LOC) UNIX-style option parser in Common Lisp. -It depends on [Bobbin][] and [split-sequence][]. - -It aims to be simple and powerful enough for the majority of use cases. +Adopt is a simple UNIX-style option parser in Common Lisp, heavily influenced by +Python's optparse and argparse. * **License:** MIT * **Documentation:** * **Mercurial:** * **Git:** +Adopt aims to be a simple, robust option parser. It can automatically print +help information and even generate `man` pages for you. + The test suite currently passes in SBCL, CCL, ECL, and ABCL on Ubuntu. Further testing is welcome. +Adopt is compatible with Quicklisp, but not *in* Quicklisp (yet). You can clone +the repository into your [Quicklisp local-projects directory][local] for now. + +The `adopt` system contains the core API and depends on [Bobbin][] and +[split-sequence][]. + +The `adopt/test` system contains the test suite, which depends on some other +systems. You don't need to load this unless you want to run the unit tests. + +[local]: https://www.quicklisp.org/beta/faq.html#local-project [Bobbin]: https://github.com/sjl/bobbin [split-sequence]: https://www.cliki.net/split-sequence diff -r cc1aad82d9f6 -r 860e10544652 src/main.lisp --- a/src/main.lisp Tue Apr 02 09:51:25 2019 -0400 +++ b/src/main.lisp Tue Apr 02 17:58:03 2019 -0400 @@ -475,13 +475,14 @@ (format stream (bobbin:wrap (help interface) width)) (format stream "~%") (dolist (group (groups interface)) - (format stream "~%~A:~%" (or (title group) (name group) "Options")) - (let* ((option-column 2) - (option-padding 2) - (doc-column (+ option-column option-width option-padding)) - (doc-width (- width doc-column))) - (dolist (option (options group)) - (print-option-help stream option option-column doc-column doc-width)))) + (when (options group) + (format stream "~%~A:~%" (or (title group) (name group) "Options")) + (let* ((option-column 2) + (option-padding 2) + (doc-column (+ option-column option-width option-padding)) + (doc-width (- width doc-column))) + (dolist (option (options group)) + (print-option-help stream option option-column doc-column doc-width))))) (let* ((examples (examples interface)) (example-column 2) (example-width (- width example-column)))