From 1ba7b003f48765c807cabfb3659e208f77d561fc Mon Sep 17 00:00:00 2001 From: Frank Duncan Date: Thu, 13 Aug 2015 05:06:41 -0500 Subject: [PATCH] Add documentation and generator. --- README.md | 71 ++++++++++++++++++++++++- bin/generatedocs.sh | 7 +++ bin/travis.lisp | 10 +++- resources/success1.lisp | 2 +- resources/success1.md | 14 ++++- src/main/docgen.lisp | 112 ++++++++++++++++++++++++++++++++++------ src/main/func.lisp | 13 +++++ src/main/package.lisp | 11 ++-- src/main/struc.lisp | 14 +++++ src/test/main.lisp | 6 +-- wiki | 2 +- 11 files changed, 235 insertions(+), 27 deletions(-) create mode 100755 bin/generatedocs.sh diff --git a/README.md b/README.md index c059be9..3d8714a 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,72 @@ # Common Lisp Document Generator -Enforcement of documentation guidelines for my Common Lisp Projects, as well as conversion to markdown for github repositories. +Enforcement of documentation guidelines for my Common Lisp Projects, as well as conversion to markdown for my github repositories. + +I wish I had aspirations for this being some standard that someone else might follow, but realistically I'm just irritated at my own laziness with regard to documentation, so I wrote a solution. The forceful nature of the validator is really just because I didn't want to write a smarter parser. As an added bonus, all the docs now look the same when I look at them in the repl, so that's kind of nice. + +## Usage + +See the [wiki](https://github.com/frankduncan/docgen/wiki) for usage information (generated by this package). + +To see how that page was created, take a look at bin/generatedocs.sh + +## Package documentation + +Packages are documented by sections broken up by one empty line, with the first section limited to 120 characters. + +## Structure/Condition documentation + +Requirements are the same as the package + +## Function documentation + +Functions should follow the template: + +```` +FUNC PATH => RESULT + + RESULT: SUCCESS-RESULTS | FAILURE-RESULT + SUCCESS-RESULTS: SUCCESS-RESULT* + SUCCESS-RESULT: (:success FILENAME) + FAILURE-RESULT: (:failure FILENAME MSG) + +ARGUMENTS AND VALUES: + + PATH: a pathname + FILENAME: the file this func was run on + MSG: a string containing the failure message + +DESCRIPTION: + + FUNC runs all the things against a file located at PATH and returns + as soon as the first func error is found. + +EXAMPLES: + + (func #P\"path/to/file.lisp\" t) => (:success \"path/to/file.lisp\") + (func #P\"path/to/error.lisp\" nil) => (:failure \"path/to/error.lisp\" \"Error msg\") +```` + +There are four sections to each function definition: + +### Header section + +Arguments should all be upper case, but &rest, &optional, and &key should be lower case. Arguments are seperated by a space. + +Results should also be upper case, and in the case of values, separated by commas. + +Types can be further elucidated by providing more information either as a list of options seperated by pipes, a tuple contained in parens, or a list denoted by an * + +### Arguments and values section: + +All the types that weren't broken down into subtypes must be explained. The form is type name in all upper case, a colon, then a description. That description can have upper case type names in it which will then get italicized later. + +### Description + +Descriptins should be indented two spaces, and not longer than 120 characters wide. Like the arguments and values, upper cased types will be italicized later. + +### Examples + +This section is optional. + +Examples are of the form: example-code => example result diff --git a/bin/generatedocs.sh b/bin/generatedocs.sh new file mode 100755 index 0000000..69ceeff --- /dev/null +++ b/bin/generatedocs.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +sbcl \ + --eval "(asdf:load-system :docgen)" \ + --eval "(format t \"----~%\")" \ + --eval "(format t \"~A\" (docgen:export-package :docgen))" \ + --eval "(quit)" 2> /dev/null | sed -n '/^----$/,$p' | tail -n +2 > wiki/Home.md diff --git a/bin/travis.lisp b/bin/travis.lisp index 6a3ed0f..608bdca 100644 --- a/bin/travis.lisp +++ b/bin/travis.lisp @@ -13,6 +13,14 @@ (when (not (syntax-checker:pretty-print-check-directory "src")) (format t "~c[1;31mFailed style check!~c[0m~%" #\Esc #\Esc) (sb-ext:exit :code 1)) +(format t "~c[1;32m- Style Passed!~c[0m~%" #\Esc #\Esc) -(format t "~c[1;32mSuccess!~c[0m~%" #\Esc #\Esc) +(format t "~%~c[1;33mChecking Docs~c[0m~%" #\Esc #\Esc) +(when (not (docgen:pretty-print-validate-packages :docgen)) + (format t "~c[1;31mFailed doc check!~c[0m~%" #\Esc #\Esc) + (sb-ext:exit :code 1)) +(format t "~c[1;32m- Doc Check Passed!~c[0m~%" #\Esc #\Esc) + +(format t "~c[1;30m--------------~c[0m~%" #\Esc #\Esc) +(format t "~c[1;32mBuild Success!~c[0m~%" #\Esc #\Esc) (sb-ext:exit :code 0) diff --git a/resources/success1.lisp b/resources/success1.lisp index 338b5b6..716dda6 100644 --- a/resources/success1.lisp +++ b/resources/success1.lisp @@ -58,7 +58,7 @@ ARGUMENTS AND VALUES: DESCRIPTION: - NOARGS runs all the things against a file and returns + RESULT-LIST runs all the things against a file and returns as soon as the first func error is found." nil) diff --git a/resources/success1.md b/resources/success1.md index c3a91d1..f0bf81d 100644 --- a/resources/success1.md +++ b/resources/success1.md @@ -4,6 +4,18 @@ This defines a simple successful package. This is should all get pulled in and the markdown.md should be equal to success1.md. +## Contents + +* **function [func-that-does-stuff](#function-func-that-does-stuff)** - _func-that-does-stuff_ runs all the things against a file and returns as soon as the first func error is found. +* **function [has-keywords](#function-has-keywords)** - _has-keywords_ runs all the things against a file and returns as soon as the first func error is found. +* **function [has-no-examples](#function-has-no-examples)** - _has-no-examples_ runs all the things against a file and returns as soon as the first func error is found. +* **function [has-optional](#function-has-optional)** - _has-optional_ runs all the things against a file and returns as soon as the first func error is found. +* **function [has-rest](#function-has-rest)** - _has-rest_ runs all the things against a file and returns as soon as the first func error is found. +* **function [noargs](#function-noargs)** - _noargs_ runs all the things against a file and returns as soon as the first func error is found. +* **function [result-list](#function-result-list)** - _result-list_ runs all the things against a file and returns as soon as the first func error is found. +* **condition [test-condition](#condition-test-condition)** - Simple documentation. +* **function [values-result](#function-values-result)** - _values-result_ runs all the things against a file and returns as soon as the first func error is found. + ## Function **FUNC-THAT-DOES-STUFF** #### Syntax: @@ -160,7 +172,7 @@ _msg_---a string containing the failure message #### Description: -NOARGS runs all the things against a file and returns as soon as the first func error is found. +_result-list_ runs all the things against a file and returns as soon as the first func error is found. ## Condition TEST-CONDITION diff --git a/src/main/docgen.lisp b/src/main/docgen.lisp index b3d251e..655b6bd 100644 --- a/src/main/docgen.lisp +++ b/src/main/docgen.lisp @@ -1,6 +1,7 @@ (in-package #:docgen) -(define-condition validation-failure nil ((msg :initarg :msg :reader validation-failure-msg))) +(define-condition validation-failure nil ((msg :initarg :msg :reader validation-failure-msg)) + (:documentation "Used internally for docgen parts to signal a validation error.")) (defun get-symb-type (symb) (cond @@ -9,21 +10,40 @@ ((documentation symb 'function) :function))) (defun validate-package (pkg) + "VALIDATE-PACKAGE PKG => FAILURES + + FAILURES: FAILURE* + FAILURE: (:failure SYMB MSG) + +ARGUMENTS AND VALUES: + + PKG: A package symbol + SYMB: Symbol the check failed on + MSG: Message containing information about the failure + +DESCRIPTION: + + VALIDATE-PACKAGE takes in PKG and validates that all the external symbols + adhere to documentation guidelines, exist, and can be parsed to be used + for exporting. + + Only one error per symbol will be reported at a time, all concatenated to + a list in the aforementioned form." (macrolet - ((with-success-check (&rest f) + ((with-success-check (symb &rest f) `(handler-case (progn ,@f :success) - (validation-failure (v) (list :failure (validation-failure-msg v)))))) + (validation-failure (v) (list :failure ,symb (validation-failure-msg v)))))) (let ((symbs nil)) (do-external-symbols (symb pkg) (push symb symbs)) (setf symbs (sort symbs #'string< :key #'symbol-name)) (remove :success (append - (list (with-success-check (docgen-pkg:doc->ast (find-package pkg)))) + (list (with-success-check pkg (docgen-pkg:doc->ast (find-package pkg)))) (mapcar (lambda (symb) - (with-success-check + (with-success-check symb (case (get-symb-type symb) (:function (docgen-func:doc->ast symb)) (:structure (docgen-struc:doc->ast symb)) @@ -31,28 +51,88 @@ symbs)))))) (defun pretty-print-validate-packages (&rest pkgs) - (mapcar - (lambda (pkg) - (let - ((failures (validate-package pkg))) - (mapcar - (lambda (failure) - (format t "In package ~A, documentation error found:~% ~A" pkg (cadr failure))) - failures) - (not failures))) - pkgs)) + "PRETTY-PRINT-VALIDATE-PACKAGES &rest PKGS => SUCCESS + + PKGS: PKG* + +ARGUMENTS AND VALUES: + + SUCCESS: Whether or not all symbols passed validation + PKG: A package symbol + +DESCRIPTION: + + PRETTY-PRINT-VALIDATE-PACKAGES takes PKGS and runs validation on all of them. + It dumps to standard out failures as it comes upon them, finally returning + whether it was successful or not. + + This can be used in travis tests to ensure that documentation can be generated + at a later date. + +EXAMPLES: + + (pretty-print-validate-packages :pkg1 :pkg2) => t" + (some + #'identity + (mapcar + (lambda (pkg) + (let + ((failures (validate-package pkg))) + (mapcar + (lambda (failure) + (format t "In ~A : ~A, documentation error found:~% ~A~%" pkg (second failure) (third failure))) + failures) + (not failures))) + pkgs))) + +(defun table-of-contents (pkg) + (format nil "## Contents~%~%~{~{* **~A [~A](#~A)** - ~A~}~%~}" + (let + ((symbs nil)) + (do-external-symbols (symb pkg) (push symb symbs)) + (setf symbs (sort symbs #'string< :key #'symbol-name)) + (mapcar + (lambda (symb) + (case (get-symb-type symb) + (:function + (list + (docgen-func:ast->category-name (docgen-func:doc->ast symb)) + (docgen-func:ast->short-name (docgen-func:doc->ast symb)) + (docgen-func:ast->link (docgen-func:doc->ast symb)) + (docgen-func:ast->short-desc (docgen-func:doc->ast symb)))) + (:structure + (list + (docgen-struc:ast->category-name (docgen-struc:doc->ast symb)) + (docgen-struc:ast->short-name (docgen-struc:doc->ast symb)) + (docgen-struc:ast->link (docgen-struc:doc->ast symb)) + (docgen-struc:ast->short-desc (docgen-struc:doc->ast symb)))))) + symbs)))) (defun export-package (pkg) + "EXPORT-PACKAGE PKG => MARKDOWN + +ARGUMENTS AND VALUES: + + PKG: A package symbol + MARKDOWN: A string containing the markdown representation of this packages documentation + +DESCRIPTION: + + EXPORT-PACKAGE takes in PKG and converts all the documentation for the symbols + into markdown with the hope of emulating the hyperspec style. + + It should only be run after the package has been validated, as it assumes that + all documentation it gets will be valid." (let ((symbs nil)) (do-external-symbols (symb pkg) (push symb symbs)) (setf symbs (sort symbs #'string< :key #'symbol-name)) (with-output-to-string (str) (format str "~A~%~%" (docgen-pkg:ast->md (docgen-pkg:doc->ast (find-package pkg)))) + (format str "~A~%" (table-of-contents pkg)) (format str "~{~A~^~%~}" (mapcar (lambda (symb) - (format t "HAHAHAH ~A ~A~%" symb (get-symb-type symb)) (case (get-symb-type symb) (:function (docgen-func:ast->md (docgen-func:doc->ast symb))) (:structure (docgen-struc:ast->md (docgen-struc:doc->ast symb))))) diff --git a/src/main/func.lisp b/src/main/func.lisp index d3540a6..b3926a1 100644 --- a/src/main/func.lisp +++ b/src/main/func.lisp @@ -328,3 +328,16 @@ (format-args-and-values (get-section :arguments-and-values)) (format-description (get-section :description)) (format-examples (get-section :examples))))) + +(defun ast->category-name (ast) + (declare (ignore ast)) + "function") + +(defun ast->short-name (ast) + (format nil "~(~A~)" (second (find :function ast :key #'car)))) + +(defun ast->link (ast) + (format nil "function-~(~A~)" (second (find :function ast :key #'car)))) + +(defun ast->short-desc (ast) + (format-text (car (cadr (find :description ast :key #'car))))) diff --git a/src/main/package.lisp b/src/main/package.lisp index 00c3bb7..119922b 100644 --- a/src/main/package.lisp +++ b/src/main/package.lisp @@ -1,11 +1,16 @@ (defpackage #:docgen (:use :cl) - (:export #:validate-package #:export-package #:validation-failure #:pretty-print-validate-packages)) + (:export #:validate-package #:export-package #:validation-failure #:pretty-print-validate-packages) + (:documentation "Main docgen package. + +Use docgen to validate that documentation strings on external symbols adhere to +a strict format and exist, so that they can be output to markdown format, while +looking decent when used within a common lisp process.")) (defpackage #:docgen-func (:use :cl) - (:export #:doc->ast #:ast->md)) + (:export #:doc->ast #:ast->md #:ast->link #:ast->short-name #:ast->short-desc #:ast->category-name)) (defpackage #:docgen-pkg (:use :cl) (:export #:doc->ast #:ast->md)) (defpackage #:docgen-struc (:use :cl) - (:export #:doc->ast #:ast->md)) + (:export #:doc->ast #:ast->md #:ast->link #:ast->short-name #:ast->short-desc #:ast->category-name)) diff --git a/src/main/struc.lisp b/src/main/struc.lisp index a616ee9..6a792ed 100644 --- a/src/main/struc.lisp +++ b/src/main/struc.lisp @@ -37,3 +37,17 @@ (first ast) (second ast) (cddr ast))) + +(defun ast->category-name (ast) + (case (first ast) + (:condition "condition") + (t "structure"))) + +(defun ast->short-name (ast) + (format nil "~(~A~)" (second ast))) + +(defun ast->link (ast) + (format nil "~(~A-~A~)" (first ast) (second ast))) + +(defun ast->short-desc (ast) + (third ast)) diff --git a/src/test/main.lisp b/src/test/main.lisp index a73dd7b..572636e 100644 --- a/src/test/main.lisp +++ b/src/test/main.lisp @@ -70,6 +70,6 @@ (defsuccesstest :success1 "resources/success1.lisp" "resources/success1.md") (deffailuretest :emptydocs "resources/emptydocs.lisp" - '((:failure "Package EMPTYDOCS has no documentation") - (:failure "Symbol NO-DOC-CONDITION has no documentation") - (:failure "Symbol NO-DOC-FUNC has no documentation"))) + `((:failure :emptydocs "Package EMPTYDOCS has no documentation") + (:failure ,(intern "NO-DOC-CONDITION" :emptydocs) "Symbol NO-DOC-CONDITION has no documentation") + (:failure ,(intern "NO-DOC-FUNC" :emptydocs) "Symbol NO-DOC-FUNC has no documentation"))) diff --git a/wiki b/wiki index 1a8916f..06c150f 160000 --- a/wiki +++ b/wiki @@ -1 +1 @@ -Subproject commit 1a8916ff9faac67cbb461da9cc939d8bee63bf25 +Subproject commit 06c150fca744ae5052741ca676e09d7081d0eefe -- 2.25.1