Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
# Emacs backup files
*~
*.fasl
30 changes: 29 additions & 1 deletion README.org
Original file line number Diff line number Diff line change
Expand Up @@ -1081,6 +1081,7 @@ The supported option kinds include:

- =counter=
- =integer=
- =float=
- =string=
- =boolean=
- =boolean/true=
Expand All @@ -1090,6 +1091,7 @@ The supported option kinds include:
- =enum=
- =list=
- =list/integer=
- =list/float=
- =filepath=
- =list/filepath=
- =switch=
Expand Down Expand Up @@ -1178,6 +1180,21 @@ argument.
:initial-value 42)
#+end_src

** Float Options

Here's an example of creating an option, which expects a float
argument.

#+begin_src lisp
(clingon:make-option
:float
:description "my float opt"
:short-name #\f
:long-name "float"
:key :my-float
:initial-value 4.2)
#+end_src

** Choice Options

=choice= options are useful when you have to limit the arguments
Expand Down Expand Up @@ -1287,6 +1304,18 @@ option, e.g.
:key :integers)
#+end_src

A similar option exists for float values using the =:list/float=
option, e.g.

#+begin_src lisp
(clingon:make-option
:list/float
:description "list of floats"
:short-name #\l
:long-name "float"
:key :floats)
#+end_src

** Switch Options

=:SWITCH= options are a variation of =:BOOLEAN= options with an
Expand Down Expand Up @@ -1917,4 +1946,3 @@ This project is Open Source and licensed under the [[http://opensource.org/licen
* Authors

- Marin Atanasov Nikolov <dnaeon@gmail.com>

3 changes: 2 additions & 1 deletion clingon.asd
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@
:bobbin
:cl-reexport
:split-sequence
:with-user-abort)
:with-user-abort
:parse-float)
:components ((:module "utils"
:pathname #P"src/"
:components ((:file "utils")))
Expand Down
68 changes: 68 additions & 0 deletions src/command.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -1172,6 +1172,74 @@ _~~A() {
(format stream "~A~%" line)))
(format stream "~%"))))

(defmethod print-documentation ((kind (eql :org)) (top-level command) stream &key (wrap-at 80))
"Prints the documentation for the given TOP-LEVEL command in Org Mode format"
(with-command-tree (node top-level)
;; Initialize command, so that options get propagated
(initialize-command node)

;; Command name
(format stream "#+TITLE: ~A~2%" (command-full-name node))

;; Print description
(cond
;; Print long description
((command-long-description node)
(let ((lines (split-sequence #\Newline
(bobbin:wrap (command-long-description node) wrap-at))))
(dolist (line lines)
(format stream "~A~%" line))
(format stream "~%")))
;; Print short description only
(t (format stream "=~A= -- ~A~2%" (command-full-name node) (command-description node))))

;; Usage info
(format stream "* Usage~2%")
(format stream "#+begin_src shell~%~A~%#+end_src~2%" (command-usage-string node))

;; Options
(when (command-options node)
(format stream "* Options~2%")
(format stream "=~A= accepts the following options:~2%" (command-full-name node))
(format stream "#+begin_src shell~%")
(print-options-usage node stream)
(format stream "#+end_src~2%"))

;; Sub-commands
(when (command-sub-commands node)
(format stream "* Sub Commands~2%")
(format stream "=~A= provides the following sub commands:~2%" (command-full-name node))
(format stream "#+begin_src shell~%")
(print-sub-commands-info node stream)
(format stream "#+end_src~2%"))

;; Examples
(when (command-examples node)
(format stream "* Examples~2%")
(dolist (example (command-examples node))
(let* ((description (car example))
(code (cdr example))
(lines (split-sequence #\Newline (bobbin:wrap description wrap-at))))
(dolist (line lines)
(format stream "~A~%" line))
(format stream "~%")
(format stream "#+begin_src shell~%~A~%#+end_src~2%" code))))

;; Authors
(when (command-authors node)
(format stream "* Authors~2%")
(dolist (author (command-authors node))
(format stream "* ~A~%" author))
(format stream "~%"))

;; License
(when (command-license node)
(format stream "* License~2%")
(let ((lines (split-sequence #\Newline (bobbin:wrap (command-license node) wrap-at))))
(dolist (line lines)
(format stream "~A~%" line)))
(format stream "~%"))))

(defmethod print-documentation ((kind (eql :mandoc)) (top-level command) stream &key (wrap-at 80))
"Generates section 1 man pages in the mdoc(7) format. Documentation
available at https://man.openbsd.org/mdoc.7"
Expand Down
91 changes: 87 additions & 4 deletions src/options.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,13 @@
:clingon.utils
:join-list)
(:import-from
:clingon.conditions
:invalid-option
:missing-required-option-value
:option-derive-error)
:parse-float
:parse-float)
(:import-from
:clingon.conditions
:invalid-option
:missing-required-option-value
:option-derive-error)
(:export
:*end-of-options-marker*
:option
Expand Down Expand Up @@ -325,6 +328,16 @@
(write-string (call-next-method) s)
(format s ":_files")))

(defclass option-csv (option-string)
()
(:documentation "An option which represents a comma-separated list"))

(defmethod make-option ((kind (eql :csv)) &rest rest)
(apply #'make-instance 'option-csv rest))

(defmethod finalize-option ((o option-csv) &key)
(setf (option-value o) (cl-ppcre:split "," (option-value o))))

;;;;
;;;; Boolean options
;;;;
Expand Down Expand Up @@ -538,6 +551,76 @@
(cons (parse-integer-or-lose arg :radix (option-integer-radix option))
(option-value option)))

;;;;
;;;; Float options
;;;;

(defun parse-float-or-lose (value &key (radix 10))
(when (floatp value)
(return-from parse-float-or-lose value))

(let ((int (parse-float value :radix radix :junk-allowed t)))
(unless int
(error 'option-derive-error :reason (format nil "Cannot parse ~A as float" value)))
int))

(defclass option-float (option)
((radix
:initarg :radix
:initform 10
:reader option-float-radix))
(:default-initargs
:parameter "INT")
(:documentation "An option class to represent an float"))

(defmethod make-option ((kind (eql :float)) &rest rest)
(apply #'make-instance 'option-float rest))

(defmethod initialize-option ((option option-float) &key)
"Initializes the float option. In case the option was
first initialized by other means, such as environment variables,
we make sure that the provided value is a valid float."
(call-next-method)

;; Nothing to initialize further
(unless (option-value option)
(return-from initialize-option))

(let ((value (option-value option)))
(setf (option-value option)
(etypecase value
(float value)
(string (parse-float-or-lose value :radix (option-float-radix option)))))))

(defmethod derive-option-value ((option option-float) arg &key)
(parse-float-or-lose arg :radix (option-float-radix option)))

(defclass option-list-float (option-list)
((radix
:initarg :radix
:initform 10
:reader option-float-radix))
(:documentation "An option which collects floats into a list"))

(defmethod make-option ((kind (eql :list/float)) &rest rest)
(apply #'make-instance 'option-list-float rest))

(defmethod initialize-option ((option option-list-float) &key)
(call-next-method)
(unless (option-value option)
(return-from initialize-option))

(setf (option-value option)
(mapcar (lambda (x)
(etypecase x
(float x)
(string (parse-float-or-lose x :radix (option-float-radix option)))))
(option-value option))))

(defmethod derive-option-value ((option option-list-float) arg &key)
(cons (parse-float-or-lose arg :radix (option-float-radix option))
(option-value option)))

;;;;
;;;; Choice/enum options
;;;;
Expand Down