commit c801a7522403541bde2f7bc309a82355ab377345 Spenser Truex <web@spensertruex.com> 2020-03-25 19:19:57 -0700 Initial commit
README.md | 29 +++++++++++++++++ manual.lisp | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ package.lisp | 3 ++ texi-macro.asd | 11 +++++++ 4 files changed, 142 insertions(+)
diff --git a/README.md b/README.md new file mode 100644 index 0000000..9ae612f --- /dev/null +++ b/README.md @@ -0,0 +1,29 @@ +# texi-macro + +Generate source documentation inside of texinfo files with short macros. + +``` tex +@c defun is a macro defined in the standard. +%%% defun +``` +Which expands to: +``` +@c defun is a macro defined in the standard. +@defmac {defun} name lambda-list &body body +Define a function at top level. +@end defmac +``` +This has been used to generate the docs for [StumpWM](https://stumpwm.github.io/). + +## Install +Requires SBCL, because `sb-introspect` is used. Support for other +implementations is welcome. + +## Hacking +Write new forms with the `defdoc` macro. + +## License + +GNU GPL 2.0 + +### _Spenser Truex <web@spensertruex.com>_ diff --git a/manual.lisp b/manual.lisp new file mode 100644 index 0000000..b183aef --- /dev/null +++ b/manual.lisp @@ -0,0 +1,99 @@ +;;;; Copyright (C) 2007-2008 Shawn Betts +;;;; +;;;; stumpwm is free software; you can redistribute it and/or modify +;;;; it under the terms of the GNU General Public License as published by +;;;; the Free Software Foundation; either version 2, or (at your option) +;;;; any later version. + +;;;; stumpwm is distributed in the hope that it will be useful, +;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;;;; GNU General Public License for more details. + +;;;; You should have received a copy of the GNU General Public License +;;;; along with this software; see the file COPYING. If not, see +;;;; <http://www.gnu.org/licenses/>. + +;;;; Commentary: +;;;; +;;;; Generate the texinfo manual from docstrings in the source. +;;;; +;;;; Code: + +(in-package #:texi-macro) + +(eval-when (:compile-toplevel :load-toplevel :execute) + (defvar *doc-fns* nil "The functions used to generate documents.") + (defun compile-doc (name body) + (concatenate 'string + "@" name " " body "~&@end " name "~%~%")) + + (defun doc-fmt (stream name body-format &rest args) + "Fill in a texinfo template." + (apply #'format stream (compile-doc name body-format) + name body-format args))) + +(defmacro defdoc ((macro name specializer &optional pprint custom-var custom-var-body) body) + "Define a document generating method." + (with-gensyms (os line sym var) + `(push (lambda (,os ,line) + (ppcre:register-groups-bind (,sym) + (,(format nil "~@{~A~}" "^" macro "\\W(.*)") ,line) + (let* ((,var (find-symbol (string-upcase ,sym) :stumpwm)) + (,var (cond ((eql ',specializer 'function) + (symbol-function ,var)) + ((eql ',specializer 'macro) + (macro-function ,var)) + (t ,var))) + (,var `(if ,',custom-var + ,(let ((custom-var sym)) + `(progn ,,@custom-var-body)) + ,var))) + (format *debug-io* "~&Formatting manual for the ~a ~a...~&" + ',var ,sym) + (let ((*print-pretty* ,pprint)) + (if (member ',specializer '(function macro)) + (doc-fmt ,os ,name ,body + ,sym + (sb-introspect:function-lambda-list ,var) + (documentation ,var ',specializer)) + (doc-fmt ,os ,name ,body + ,sym + (documentation ,var ',specializer)))) + t))) + *doc-fns*))) + +(defdoc ("@@@" "defun" function t + name + (if (find #\( name :test 'char=) + ;; handle (setf <symbol>) functions + (with-standard-io-syntax + (let ((*package* (find-package :stumpwm))) + (fdefinition (read-from-string name)))) + (symbol-function (find-symbol (string-upcase name) :stumpwm)))) + "{~a} ~{~a~^ ~}~%~a") + +(defdoc ("%%%" "defmac" function) "{~a} ~{~a~^ ~}~%~a") +(defdoc ("###" "defvar" variable nil) "~a~%~a") +(defdoc ("\\$\\$\\$" "defvr" variable nil) "{Hook} ~a~%~a") +(defdoc ("!!!" "deffn" function) "{Command} ~a ~{~a~^ ~}~%~a") + +(defun generate (os line) + "Generate a texi.in documentation line." + (dolist (fn *doc-fns*) + (when (funcall fn os line) + (return-from generate))) + ;; Not a macro line. + (write-line line os)) + +(defun generate-manual (&key in out (package (find-package :cl))) + #.(format nil "~@{~a~^~%~}" + "Generate the texinfo manual from the template texi.in file." + "IN the input file path file.texi.in" + "OUT the output file path file.texi" + "PACKAGE is the package where names are pulled from") + (let ((*print-case* :downcase)) + (with-open-file (os out :direction :output :if-exists :supersede) + (with-open-file (is in :direction :input) + (loop for line = (read-line is nil is) + until (eq line is) do (generate os line)))))) diff --git a/package.lisp b/package.lisp new file mode 100644 index 0000000..b3ece35 --- /dev/null +++ b/package.lisp @@ -0,0 +1,3 @@ +(defpackage :texi-macro + (:use :cl) + (:import-from :sb-introspect :function-lambda-list)) diff --git a/texi-macro.asd b/texi-macro.asd new file mode 100644 index 0000000..b876f26 --- /dev/null +++ b/texi-macro.asd @@ -0,0 +1,11 @@ +;;;; texi-macro.asd + +(asdf:defsystem #:texi-macro + :description "Texinfo sigils for Common Lisp documentation." + :author "Spenser Truex <web@spensertruex.com>" + :license "GNU GPL v3" + :version "0.0.1" + :serial t + :depends-on (#:sb-introspect) + :components ((:file "package") + (:file "manual")))