

Or see the list of project sponsors.
| Documentation | 😀 | 
| Docstrings | 😀 | 
| Tests | 😀 | 
| Examples | 😀 | 
| RepositoryActivity | 🥺 | 
| CI | 🥺 | 
This is yet another project by Max Rottenkolber (@eugeneia_). Geneva is the documentation system. It includes a few subsystems:
Geneva separates a document structure from its representation.
Core package provides the way to define a document's structure using calls to Lisp functions:
POFTHEDAY> (geneva:make-document
            (list
             (geneva:make-section (list "First section")
                (list
                 (geneva:make-paragraph (list "Foo bar"))
                 (geneva:make-paragraph (list "Blah minor"))))
             (geneva:make-section (list "Second section")
                 (list
                  (geneva:make-paragraph (list (geneva:make-bold "Hello World!")))))))
((:SECTION ("First section")
  ((:PARAGRAPH ("Foo bar"))
   (:PARAGRAPH ("Blah minor"))))
 (:SECTION ("Second section")
  ((:PARAGRAPH ((:BOLD "Hello World!"))))))
POFTHEDAY> (geneva.html:render-html * :index-p nil)As you can see, the document is a bunch of Lisp lists. It easily can be rendered into the HTML:
<SECTION>
  <HEADER>
    <A NAME="section-1">
      <H1><SPAN CLASS="geneva-index">1</SPAN> First section</H1>
    </A>
  </HEADER>
  <P>Foo bar</P>
  <P>Blah minor</P>
</SECTION>
<SECTION>
  <HEADER>
    <A NAME="section-2">
      <H1><SPAN CLASS="geneva-index">2</SPAN> Second section</H1>
    </A>
  </HEADER>
  <P><B>Hello World!</B></P>
</SECTION>Foo bar
Blah minor
Hello World!
Or you might want to render it into the plain text:
POFTHEDAY> (geneva.plain-text:render-plain-text * :index-p nil)
1 First section
   Foo bar
   Blah minor
2 Second section
   Hello World!Humans usually prefer to use specialized markup languages. Geneva provides MK2 markup language to define a rich text. For example, text from the previous example can be written like that:
POFTHEDAY> (let ((text "
< First section
  Foo bar
  Blah minor
>
< Second section
  *Hello World!*
>"))
             (with-input-from-string (s text)
               (geneva.mk2:read-mk2 s)))
((:SECTION ("First section")
  ((:PARAGRAPH ("Foo bar"))
   (:PARAGRAPH ("Blah minor"))))
 (:SECTION ("Second section")
  ((:PARAGRAPH ((:BOLD "Hello World!"))))))There is also a system to process documentation strings into the Geneva document. It can be used to render documentation for your own system. Docstrings can be written in MK2 markup.
Now we'll create a test Lisp package and fill it with a variable, function and macro.
POFTHEDAY> (defpackage foo
             (:use #:cl)
             (:documentation "This is an example
                              of the package.
                              Documentation can be written in *MK2 format*.
                              And include _rich_ text with links."))
POFTHEDAY> (defun foo::bar (minor)
             "Makes some tranformation.
              *Arguments*:
              {minor} - an object to transform."
             ;; do the job
             (values))
POFTHEDAY> (defvar foo::*blah* :blah
             "Switches between two modes: {:blah} and {:minor}")
POFTHEDAY> (defmacro foo::with-minor (&body body)
             "Runs {body} with {:minor} mode applied."
             `(let ((foo::*blah* :minor))
                ,@body))
POFTHEDAY> (export '(foo::*blah* foo::bar foo::with-minor)
                   :foo)Now we can build documentation for this package in two simple steps:
POFTHEDAY> (geneva.cl:api-document :foo)
((:SECTION ("foo (Package)")
  ((:PARAGRAPH ("This is an example of the package."))
   (:PARAGRAPH ("Documentation can be written in " (:BOLD "MK2 format") "."))
   (:PARAGRAPH ("And include " (:ITALIC "rich") " text with links."))
   (:SECTION ("*blah* (Variable)")
    ((:PARAGRAPH ((:BOLD "Initial Value:")))
     (:PARAGRAPH ((:FIXED-WIDTH ":BLAH")))
     (:PARAGRAPH
      ("Switches between two modes: " (:FIXED-WIDTH ":blah") " and "
       (:FIXED-WIDTH ":minor")))))
   (:SECTION ("bar (Function)")
    ((:PARAGRAPH ((:BOLD "Syntax:")))
     (:PARAGRAPH ("— Function: " (:BOLD "bar") " " (:ITALIC "minor")))
     (:PARAGRAPH ("Makes some tranformation."))
     (:PARAGRAPH ((:BOLD "Arguments") ":"))
     (:PARAGRAPH ((:FIXED-WIDTH "minor") " - an object to transform."))))
   (:SECTION ("with-minor (Macro)")
    ((:PARAGRAPH ((:BOLD "Syntax:")))
     (:PARAGRAPH
      ("— Macro: " (:BOLD "with-minor") " " (:FIXED-WIDTH "&body") " "
       (:ITALIC "body")))
     (:PARAGRAPH
      ("Runs " (:FIXED-WIDTH "body") " with " (:FIXED-WIDTH ":minor")
       " mode applied.")))))))
POFTHEDAY> (geneva.html:render-html * :index-p nil)It will render this HTML:
This is an example of the package.
Documentation can be written in MK2 format.
And include rich text with links.
Initial Value:
:BLAH
Switches between two modes: :blah and :minor
Syntax:
— Function: bar minor
Makes some tranformation.
Arguments:
minor - an object to transform.
Syntax:
— Macro: with-minor &body body
Runs body with :minor mode applied.
Of cause, you can provide your own CSS stylesheets to make the page looks like you want.
I think Geneva might become a great replacement to reStructured text for documentation of my own libraries. Thank you, @eugeneia_!.
Though, it would be wonderful to add a little extensibility and ability to cross-referencing between different documentation sections.
By the way, in Geneva's sources I found an interesting way to keep DRY principle. This piece of code reuses (content-values text-token) 5 times.
(defun render-text (text)
  "Render TEXT as HTML."
  (dolist (text-token text)
    (ecase (content-type text-token)
      (:plain (text #1=(content-values text-token)))
      (:bold (b #1#))
      (:italic (i #1#))
      (:fixed-width (code #1#))
      (:url (multiple-value-bind (string url) #1#
              (a [:href (or url string)] (or string url)))))))I don't know the proper name of the Lisp's feature, but it allows to refer to the piece of data-structure defined earslier. The most common usage I've seen before is a circular list's definition:
POFTHEDAY> '#1=(1 2 3 . #1#)
(1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3
 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3
 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3
 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3
 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3
 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3
 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3
 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3
 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3
 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3
 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3
 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3
 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3
 1 2 3 1 2 ...)I've created an example project which uses Geneva and MK2 markup to build a documentation.
It also shows how to render docs using GitHub Actions.