502eaba9e114

Add some docstrings
[view raw] [browse files]
author Steve Losh <steve@stevelosh.com>
date Tue, 21 May 2019 22:51:10 -0400
parents 4921979d07a8
children 1a81296fb3ec
branches/tags (none)
files src/main.lisp src/package.lisp

Changes

diff -r 4921979d07a8 -r 502eaba9e114 src/main.lisp
--- a/src/main.lisp	Tue May 21 22:05:24 2019 -0400
+++ b/src/main.lisp	Tue May 21 22:51:10 2019 -0400
@@ -55,9 +55,9 @@
 (defun 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.
+  This is not implemented for every Common Lisp implementation.  You can pass
+  your own values to `parse-options` and `print-help` if it's not implemented
+  for your particular Lisp.
 
   "
   #+sbcl sb-ext:*posix-argv*
@@ -67,6 +67,13 @@
   #-(or sbcl ccl) (error "ARGV is not supported on this implementation."))
 
 (defun exit (&optional (code 0))
+  "Exit the program with status `code`.
+
+  This is not implemented for every Common Lisp implementation.  You can write
+  your own version of it and pass it to `print-help-and-exit` and
+  `print-error-and-exit` if it's not implemented for your particular Lisp.
+
+  "
   #+sbcl (sb-ext:exit :code code)
   #+ccl (ccl:quit code)
   #-(or sbcl ccl) (error "EXIT is not supported on this implementation."))
@@ -121,10 +128,38 @@
                     manual
                     parameter
                     reduce
+                    ;; can't just default to nil because multiple options might
+                    ;; have the same result key, and only one can provide init
+                    (initial-value nil initial-value?)
                     (result-key name)
-                    (initial-value nil initial-value?)
                     (key #'identity)
                     (finally #'identity))
+  "Create and return an option, suitable for use in an interface.
+
+  This function takes a number of arguments, some required, that define how the
+  option interacts with the user.
+
+  * `name` (**required**): a symbol naming the option.
+  * `help` (**required**): a short string describing what the option does.
+  * `result-key` (optional): a symbol to use as the key for this option in the hash table of results.
+  * `long` (optional): a string for the long form of the option (e.g. `--foo`).
+  * `short` (optional): a character for the short form of the option (e.g. `-f`).  At least one of `short` and `long` must be given.
+  * `manual` (optional): a string to use in place of `help` when rendering a man page.
+  * `parameter` (optional): a string.  If given, it will turn this option into a parameter-taking option (e.g. `--foo=bar`) and will be used as a placeholder
+  in the help text.
+  * `reduce` (**required**): a function designator that will be called every time the option is specified by the user.
+  * `initial-value` (optional): a value to use as the initial value of the option.
+  * `key` (optional): a function designator, only allowed for parameter-taking options, to be called on the values given by the user before they are passed along to the reducing function.  It will not be called on the initial value.
+  * `finally` (optional): a function designator to be called on the final result after all parsing is complete. 
+
+  The manner in which the reducer is called depends on whether the option takes a parameter:
+
+  * For options that don't take parameters, it will be called with the old value.
+  * For options that take parameters, it will be called with the old value and the value given by the user.
+
+  See the full documentation for more information.
+
+  "
   (when (and (null long) (null short))
     (error "Option ~A requires at least one of :long/:short." name))
   (when (null reduce)
@@ -143,18 +178,18 @@
                manual (or null string)
                parameter (or null string))
   (apply #'make-instance 'option
-         :name name
-         :result-key result-key
-         :help help
-         :manual manual
-         :long long
-         :short short
-         :parameter parameter
-         :reduce reduce
-         :key key
-         :finally finally
-         (when initial-value?
-           (list :initial-value initial-value))))
+    :name name
+    :result-key result-key
+    :help help
+    :manual manual
+    :long long
+    :short short
+    :parameter parameter
+    :reduce reduce
+    :key key
+    :finally finally
+    (when initial-value?
+      (list :initial-value initial-value))))
 
 (defun optionp (object)
   (typep object 'option))
@@ -168,10 +203,28 @@
     (format stream "~A (~D options)" (name g) (length (options g)))))
 
 (defun make-group (name &key title help manual options)
-  (check-types title (or null string)
+  "Create and return an option group, suitable for use in an interface.
+
+  This function takes a number of arguments that define how the group is
+  presented to the user:
+
+  * `name` (**required**): a symbol naming the group.
+  * `title` (optional): a title for the group for use in the help text.
+  * `help` (optional): a short summary of this group of options for use in the help text.
+  * `manual` (optional): used in place of `help` when rendering a man page.
+  * `options` (**required**): the options to include in the group.
+
+  See the full documentation for more information.
+
+  "
+  (check-types name symbol
+               title (or null string)
                help (or null string)
                manual (or null string)
                options list)
+  (assert (every #'optionp options) (options)
+    "The :options argument to ~S was not a list of options.  Got: ~S"
+    'make-group options)
   (make-instance 'group
     :name name
     :title title
@@ -211,6 +264,22 @@
             (length (groups i)))))
 
 (defun make-interface (&key name summary usage help manual examples contents)
+  "Create and return a command line interface.
+
+  This function takes a number of arguments that define how the interface is
+  presented to the user:
+
+  * `name` (**required**): a symbol naming the interface.
+  * `summary` (**required**): a string of a concise, one-line summary of what the program does.
+  * `usage` (**required**): a string of a UNIX-style usage summary, e.g. \"[OPTIONS] PATTERN [FILE...]\".
+  * `help` (**required**): a string of a longer description of the program.
+  * `manual` (optional): a string to use in place of `help` when rendering a man page.
+  * `examples` (optional): an alist of `(prose . command)` conses to render as a list of examples.
+  * `contents` (optional): a list of options and groups.  Ungrouped options will be collected into a single top-level group.
+
+  See the full documentation for more information.
+
+  "
   (check-types name string
                summary string
                usage string
@@ -246,6 +315,7 @@
                  (setf (gethash long (long-options interface)) option)))))
       (dolist (g groups)
         (map nil #'add-option (options g))))
+    ;; TODO: check for multiple conflicting initial-values
     interface))
 
 
@@ -532,6 +602,7 @@
      (width 80)
      (option-width 20)
      (include-examples t)
+     (exit-function #'exit)
      (exit-code 0))
   "Print a pretty help document for `interface` to `stream` and exit.
 
@@ -541,17 +612,19 @@
       (when (gethash 'help options)
         (print-help-and-exit *ui*))
       (run arguments options))
+
   "
   (print-help interface
-               :stream stream
-               :program-name program-name
-               :width width
-               :option-width option-width
-               :include-examples include-examples)
-  (exit exit-code))
+              :stream stream
+              :program-name program-name
+              :width width
+              :option-width option-width
+              :include-examples include-examples)
+  (funcall exit-function exit-code))
 
 (defun print-error-and-exit (error &key
                              (stream *error-output*)
+                             (exit-function #'exit)
                              (exit-code 1)
                              (prefix "error: "))
   "Print `prefix` and `error` to `stream` and exit.
@@ -566,7 +639,7 @@
 
   "
   (format stream "~A~A~%" (or prefix "") error)
-  (exit exit-code))
+  (funcall exit-function exit-code))
 
 
 ;;;; Man ----------------------------------------------------------------------
@@ -610,6 +683,16 @@
 (defun print-manual (interface &key
                      (stream *standard-output*)
                      (manual-section 1))
+  "Print a troff-formatted man page for `interface` to `stream`.
+
+  Example:
+
+    (with-open-file (manual \"man/man1/foo.1\"
+                            :direction :output
+                            :if-exists :supersede)
+      (print-manual *ui* manual))
+
+  "
   (check-type manual-section (integer 1))
   (labels
       ((f (&rest args)
diff -r 4921979d07a8 -r 502eaba9e114 src/package.lisp
--- a/src/package.lisp	Tue May 21 22:05:24 2019 -0400
+++ b/src/package.lisp	Tue May 21 22:51:10 2019 -0400
@@ -24,7 +24,6 @@
     :supply-new-value
 
     :flip
-    :oldest
     :collect
     :first
     :last