860e10544652

Start the actual documentation
[view raw] [browse files]
author Steve Losh <steve@stevelosh.com>
date Tue, 02 Apr 2019 17:58:03 -0400
parents cc1aad82d9f6
children 0431c573d371 7f0abaf11050
branches/tags (none)
files LICENSE.markdown README.markdown docs/01-installation.markdown docs/01-usage.markdown docs/02-reference.markdown docs/02-usage.markdown docs/03-changelog.markdown docs/03-reference.markdown docs/04-changelog.markdown docs/api.lisp docs/index.markdown src/main.lisp

Changes

--- 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
--- 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:** <https://sjl.bitbucket.io/adopt/>
--- 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
--- /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")
+    #<HASH-TABLE :TEST EQL :COUNT 3 {10103142A3}>
+
+`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
+----------------------------
+
--- /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`
+
--- 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)`.
--- /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.
--- 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
-
-  
-
--- 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.
--- 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")
--- 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:** <https://sjl.bitbucket.io/adopt/>
 * **Mercurial:** <https://bitbucket.org/sjl/adopt/>
 * **Git:** <https://github.com/sjl/adopt/>
 
+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
--- 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)))