1 Example of Geneva documentation for a Common Lisp library

This is a small library includes a few functions with docstrings and a documentation for the system and all included packages.

The purpose is to demonstrate core features of the Geneva documentation system.

The repository can be used as a template for new libraries if you've choosen Geneva for documenting your library.

Let's review features, provided by Geneva.

1.1 Pros & Cons of Coo

1.1.1 Pros

  • Geneva separates phases of the document construction and rendering. Documents can be written in many formats and all you need is a parser which will return the documennt as a nested list structure like:
((:SECTION ("First section")
  ((:PARAGRAPH ("Foo bar"))
   (:PARAGRAPH ("Blah minor"))))
 (:SECTION ("Second section")
  ((:PARAGRAPH ((:BOLD "Hello World!"))))))
  • Docstrings can be written in mk2 format.

1.1.2 Cons

  • There is no a function to build documentation for your system. You have to define it manually or to create a separate library which will find documents, extract docstrings, etc.
  • Geneva includes mk2 markup format which is very strange. To use Markdown or reStructured text you'll need to write a separate parser.
  • There is no cross-reference facility, but probably it could be implemented as part of the geneva.cl:api-document.
  • geneva.cl:api-document works with packages and it will be hard to use it for building documentation for a package infrerred ASDF system.
  • It is impossible to reference documentation sections.
  • There is no builtin HTML themes.
  • Types of inner document elements are hardcoded and it is impossible to add new blocks use some hooks to change the way how the document is rendered.
  • Code blocks don't support highlighting:
(defun render-content (content level)
  "Render CONTENT as HTML."
  (case (content-type content)
    (:paragraph (render-paragraph content))
    (:listing   (render-listing content))
    (:table     (render-table content))
    (:media     (render-media content))
    (:plaintext (render-plaintext content))
    (:section   (render-section content level))
    (t (error "Invalid content type in CONTENT: ~S."
	      (content-type content)))))

1.2 Handwritten documentation

I think the ability to write a large pieces of documentation which aren't bound to a function, class or module is an important feature. This way you can tell the user about some toplevel abstractions and give a bird eye view on the library or system.

For example, handwritten parts of the documentation can provide some code snippets to demonstrate the ways, how to use the library:

(loop repeat 42
     collect (foo "bar" 100500))

And when you are talking about some function or class, you can reference it. For example, if I'm talking about foo function, I want to be able to reference it.

But Geneva does not support cross referencing. And you have to write links manually: [example/app:foo](example/app.html#section-1-1) and it will appear in the code as the link example/app:foo

1.3 Extending Geneva

Geneva consists of usual functions and can't be easily hacked around. However, you can do some document processing before the rendering.

1.4 Autogenerated API

Geneva provides a function geneva.cl:api-document to create a document for a Lisp package. For example, it is used by this example project to build docs for two subpackages:

(flet ((document-package (package)
           (check-type package keyword)
           (let ((filename (format nil "docs/build/~A.html"
                                   (string-downcase package))))
             (ensure-directories-exist filename)
             (uiop:with-output-file (s filename
                                       :if-exists :supersede)
               (geneva.html:render-html (geneva.cl:api-document package)
                                        :stream s)))))
    (document-package :example/app)
    (document-package :example/utils))

Here is the results:

For a package infrred ASDF system a function can be written, which will discover all subpackages.