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.