# HG changeset patch # User Steve Losh # Date 1558493470 14400 # Node ID 502eaba9e114621c0abd45257a77d5f13eeed8c7 # Parent 4921979d07a8918d9acf7e0331c9bc3be7629200 Add some docstrings 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