Skip to content

listx/lilac

Repository files navigation

Lilac

Introduction

Lilac is an addon for Emacs Org mode to make Literate Programming (LP) using <a href=”glossary: Noweb”>Noweb-style [cite:@ramsey_1994] references easy. It comes in two parts:

  1. lilac.el: to be loaded and used when /weaving/ [cite:@knuth_1984 98] and /tangling/ [cite:@knuth_1984 98] from your own Org mode files (which use Noweb references), and
  2. lilac.theme: to be sourced by your Org mode files to use Lilac’s CSS and JavaScript on top of the HTML export.

This document itself reuses the lilac.theme file, which gives you a preview (if you are viewing the HTML output) of what weaving with Lilac looks like.

NOTE: Do not view this page using GitHub’s default Org file viewer, because many sections will appear broken. Instead, please view this page by visiting https://funloop.org/lilac, or by cloning the repository and pointing your browser to the top-level index.html file directly.

Motivation and differences vs Org defaults

Lilac sacrifices some degree of customization in Org mode in order to make it easier to write literate programs using Noweb-style references. Lilac also focuses on HTML export for weaving.

The following are some examples of how things are done in the default Org mode settings, and how Lilac does things differently.

Source code block captions

Org defaults

By default, source code blocks do not get any captions in the HTML output. This can be an annoyance for LP because you have to manually explain the name of each block to keep track of their usage in other blocks (when referring to them with Noweb style references).

Lilac

Lilac frees you from having to write a custom #+caption: ... text for every source code block, because it generates them based on the name of the Noweb reference. Lilac links child blocks and parent blocks together so that you can click on code block names to navigate around this parent/child hierarchy.

Source code block links

Org defaults

While Org mode allows source code blocks to refer to other blocks (Noweb references), it does not do any kind of linking. So you can write blocks like

#+name: example-parent-block
#+begin_src bash
echo "Hello from the parent block"
<<some-other-block>>
#+end_src

...

#+name: some-child-block
#+begin_src bash
echo "I am child 1"
#+end_src

but when this is exported to HTML neither example-parent-block nor some-child-block have any links to each other. This is frustrating because it makes navigating between them difficult.

Org mode does not create link anchors for headings, which makes referring to them from other external documents slightly annoying. Org mode does not add link anchors to source code blocks.

Lilac

Lilac automatically links child blocks and parent blocks together so that you can click on code block names to navigate around this parent/child hierarchy.

Also, link anchors are added to all source code blocks as well as headlines, and they are saved in your browser’s history as you click around, such that you can go back/forward and be taken back to these intra-document locations. This makes intra-document navigation more structured to help prevent you from getting lost.

Link IDs

Org defaults

Org mode generates random link ID’s on every export. That is, even if you change nothing in your Org mode file, if you export to HTML there will be a diff because all of the linked objects will get a new link ID.

Lilac

Lilac makes HTML generation deterministic. This makes it VCS and CI-friendly because the output changes if and only if the input changes. Speaking of CI-friendliness, Lilac was designed to be run from outside of Emacs by default (and not interactively).

HTML Table of Contents

Org defaults

When exporting to HTML, the Table of Contents (TOC) is at the top of the document.

Lilac

Lilac puts the TOC in a sidebar on the left. It also uses some JavaScript to check where the current mouse position is to update the current location in the TOC. That is, the TOC follows you as you scroll down the document.

Side notes, aka “margin notes”

Org defaults

Side notes are used to insert notes on the right-hand-side margin, and are inspired by the ones found in Donald Knuth’s books.

They are not supported in Org mode.

Lilac

Lilac supports the #+begin_sidenote and #+end_sidenote delimiters. Text inside such delimiters are shown on the right hand margin.

User Guide

First create an Org mode file that uses LP. Then either clone this repo or add it as a submodule to your project. The point is to get the lilac.el (and its dependencies, themselves submodules) and lilac.theme (and associated CSS/JavaScript files) available locally. Then for the Org mode file you are using, you can load lilac.el and run (lilac-publish) to generate the HTML file, or run (org-babel-tangle) to generate the source code from it. See the Makefile that this project uses as a reference.

The main thing that you need to keep in mind when writing Org mode files for consumption by Lilac is that the source code blocks must use Noweb references using __NREF__ as a prefix. See the discussion below.

Source code blocks: monoblocks and polyblocks

In Org mode, Noweb-style references by default must be enclosed in double angle brackets << and >>. While this works, it’s problematic because it can mean different things syntactically based on the source code language. Your source code block’s language might think that the angle brackets are operators or keywords and colorize them differently, for example.

Instead, Lilac expects Noweb-style references in the form __NREF__foo (where the “NREF” stands for “Noweb reference”). Then you are free to name your child block with this same __NREF__foo name. This is better because now you can search for this word __NREF__foo in your raw Org mode document and you’ll instantly be able to see where it is used. Contrast this with the default Org mode behavior where you’ll have to search for <<foo>> and foo separately (because searching for just foo may collide with other names that are not source code block names).

In the example below, the parent-block refers to 2 other child blocks for its definition.

#+name: parent-block
#+begin_src bash
echo "Hello from the parent block"
__NREF__child-block-1
__NREF__child-block-2
#+end_src

...

#+name: __NREF__child-block-1
#+begin_src bash
echo "I am child 1"
#+end_src

...

#+header: :noweb-ref __NREF__child-block-2
#+begin_src bash
echo -n "I am "
#+end_src

#+header: :noweb-ref __NREF__child-block-2
#+begin_src bash
echo "child 2"
#+end_src

This example illustrates the two ways to define a child block: either as a single code block with #+name: __NREF__foo, or as multiple blocks with #+header: :noweb-ref __NREF__foo. Lilac calls them monoblocks and polyblocks respectively. Polyblocks are concatenated together in the order they appear in the overall Org file; this final concatenated version is what gets inserted into the Noweb reference in the parent block.

Examples of source code blocks

Below is an example of the usage described above. Notice how the child blocks referenced in the parent block are linked to their definitions.

echo "Hello from the parent block"
__NREF__example-child-block-foo
echo ""
__NREF__example-child-block-bar

Below are the child blocks. The first is a monoblock. Every child block’s name links back up to the parent block where it is referenced.

echo "I am child 1"

The blocks below are polyblocks.

echo -n "I am "

Note the fraction after the name which denotes the position of the block in the overall polyblock “chain”.

echo "child 2"

Multi-parent child blocks

Sometimes a piece of code will be reused in different source code blocks. You can think of it as constants in a programming language, but in LP the concept extends to any arbitrary piece of text.

Below is an example where we define a child block once…

echo "Hello from child block baz"

…but where we also reuse it (reference it) in different parent blocks.

echo "Hello from parent 1"
__NREF__example-child-block-baz
echo "Hello from parent 2"
__NREF__example-child-block-baz

Notice that the child block has an additional link named “2”, which points to the second parent that references this block (the first parent is linked by the child block name itself, as in the previous example). If it is referenced in additional parent blocks, they will show up as links “3”, “4”, etc.

Examples

Here we have some examples of how certain Org mode features look when exported to HTML with Lilac. Each section has an orgmode input and HTML output. The input is what you type into your *.org (plaintext) file, and the output is what you get in HTML after it is rendered with Lilac’s publishing function, (lilac-publish).

Code blocks

Source blocks

Plain

Orgmode input:

#+begin_src python
def foo():
    return 42
#+end_src

Rendered HTML output:

def foo():
    return 42
Monoblock

Orgmode input:

#+caption: A parent block.
#+begin_src python
def foo():
    __NREF__ex_monoblock
    return 42
#+end_src

#+name: __NREF__ex_monoblock
#+begin_src python
print("hello world")
#+end_src

Rendered HTML output:

def foo():
    __NREF__ex_monoblock
    return 42
print("hello world")
Monoblock (evaluation result’s value)

Sometimes it’s useful to evaluate code and to use the resulting value. This is in contrast to always inserting the contents of the source code blocks literally.

However you must make sure that the referenced source code block’s programming language is added to the list of org-babel-do-load-languages. Otherwise the evaluation will be forbidden.

Orgmode input:

#+name: __NREF__ex_evaluate_me
#+begin_src python :exports none
return 4 * 10 + 2
#+end_src

#+caption: This uses an evaluation result.
#+begin_src python :noweb yes
print("The answer is __NREF__ex_evaluate_me()")
#+end_src

Rendered HTML output:

print("The answer is __NREF__ex_evaluate_me()")
Monoblock (pass argument to source code block)

Here we have an example of passing in an argument to a source code block.

Orgmode input:

#+name: __NREF__ex_take_argument
#+begin_src python :exports none :var my_var="foo"
return "Hello, my name is " + my_var
#+end_src

#+caption: Passing string args.
#+begin_src python :noweb yes
print("Call without explicit argument: __NREF__ex_take_argument()")
print("Call with explicit argument: __NREF__ex_take_argument(my_var="bar")")
#+end_src

Rendered HTML output:

print("Call without explicit argument: __NREF__ex_take_argument()")
print("Call with explicit argument: __NREF__ex_take_argument(my_var="bar")")
Polyblock

Orgmode input:

#+caption: A parent block.
#+begin_src python
def foo():
    __NREF__ex_polyblock
    return 42
#+end_src

#+header: :noweb-ref __NREF__ex_polyblock
#+begin_src python
print("hello world")
#+end_src

Some intervening text.

#+header: :noweb-ref __NREF__ex_polyblock
#+begin_src python
print("abcdefg")
#+end_src

Rendered HTML output:

def foo():
    __NREF__ex_polyblock
    return 42
print("hello world")

Some intervening text.

print("abcdefg")
Linking to a line in the code block body

Orgmode input:

#+begin_src python -r -l "#ref:%s"
print("foo")
print("hi")     #ref:embedded-link
print("bar")
#+end_src

The line at [[(embedded-link)][=embedded-link=]] prints "hi".

Rendered HTML output:

print("foo")
print("hi")     #ref:embedded-link
print("bar")

The line at =embedded-link= prints “hi”.

Example blocks

Orgmode input:

#+begin_example
Hello world.
Foo bar.
#+end_example

Rendered HTML output:

Hello world.
Foo bar.

Quote blocks

Orgmode input:

#+begin_quote
Hello world.
Foo bar.
#+end_quote

Rendered HTML output:

Hello world. Foo bar.

Side notes

Note that Lilac only adds some special CSS styling to side notes. Orgmode is generous with source code blocks and accepts any #+begin_foo and #+end_foo lines.

Orgmode input:

#+begin_sidenote
Hello world.
Foo bar.
#+end_sidenote

Rendered HTML output (see right hand margin):

Math (MathJax)

This is not a Lilac feature (it’s just part of Org mode), but it’s worth demonstrating how it looks.

Orgmode input:

The equation \( E = mc^2 \) is probably the most famous equation in the world,
perhaps more famous than the Pythagorean theorem

\[
a^2 + b^2 = c^2.
\]

The equation

\[
\frac{\pi^2}{6} = \sum_{n=1}^\infty \frac{1}{n^2} = \
\frac{1}{1^2} + \frac{1}{2^2} + \frac{1}{3^2} + \cdots
\]

was first demonstrated by Leonhard Euler in 1734. Here's another equation,

#+name: eqn:1
\begin{equation}
\pi = \sum_{k = 0}^{\infty}\left[\frac{1}{16^k} \
\left(\frac{4}{8k+1}-\frac{2}{8k+4}-\frac{1}{8k + 5}-\frac{1}{8k+6}\right)\right]
\end{equation}

which is known as the Bailey-Borwein-Plouffe formula and can compute arbitrary
digits of \(\pi\). A variant of this equation is as follows

#+name: eqn:2
\begin{equation}
\pi = \frac1{2^6} \sum_{n=0}^\infty \frac{(-1)^n}{2^{10n}} \
\left(-\frac{2^5}{4n+1} - \frac1{4n+3} + \frac{2^8}{10n+1} - \
\frac{2^6}{10n+3} - \frac{2^2}{10n+5} - \frac{2^2}{10n+7} + \
\frac1{10n+9} \right)
\end{equation}

and is known as the Bellard's formula. Equation [[eqn:2]] is too wide so
MathJax breaks it over multiple lines. Below is a version where we do the line
breaking ourselves.

\begin{equation}
\pi = \frac1{2^6} \sum_{n=0}^\infty \frac{(-1)^n}{2^{10n}} \
\left(-\frac{2^5}{4n+1} - \frac1{4n+3} + \frac{2^8}{10n+1} - \\
\frac{2^6}{10n+3} - \frac{2^2}{10n+5} - \frac{2^2}{10n+7} + \
\frac1{10n+9} \right)
\end{equation}

Rendered HTML output:

The equation \( E = mc^2 \) is probably the most famous equation in the world, perhaps more famous than the Pythagorean theorem

\[ a^2 + b^2 = c^2. \]

The equation

\[ \frac{π^2}{6} = ∑n=1^∞ \frac{1}{n^2} = \ \frac{1}{1^2} + \frac{1}{2^2} + \frac{1}{3^2} + \cdots \]

was first demonstrated by Leonhard Euler in 1734. Here’s another equation,

\begin{equation} π = ∑k = 0\left[\frac{1}{16^k} \ \left(\frac{4}{8k+1}-\frac{2}{8k+4}-\frac{1}{8k + 5}-\frac{1}{8k+6}\right)\right] \end{equation}

which is known as the Bailey-Borwein-Plouffe formula and can compute arbitrary digits of \(π\). A variant of this equation is as follows

\begin{equation} π = \frac1{2^6} ∑n=0^∞ \frac{(-1)^n}{210n} \ \left(-\frac{2^5}{4n+1} - \frac1{4n+3} + \frac{2^8}{10n+1} - \ \frac{2^6}{10n+3} - \frac{2^2}{10n+5} - \frac{2^2}{10n+7} + \ \frac1{10n+9} \right) \end{equation}

and is known as the Bellard’s formula. Equation eqn:2 is too wide, though. Below is a version where we insert manual line breaks.

\begin{equation} π = \frac1{2^6} ∑n=0^∞ \frac{(-1)^n}{210n} \ \left(-\frac{2^5}{4n+1} - \frac1{4n+3} + \frac{2^8}{10n+1} -
\frac{2^6}{10n+3} - \frac{2^2}{10n+5} - \frac{2^2}{10n+7} + \ \frac1{10n+9} \right) \end{equation}

Known limitations

These limitations stem from the fact that they are not natively supported by Org mode.

Source code blocks must reference other blocks in the same file
If you’re using source code blocks, and they refer to other blocks, those referenced blocks must be defined in the same Org file. In practice this isn’t a big problem.

Developer Guide

If you are interested in contributing back to this project, or if you are curious to see how everything works, have a look at the Developer Guide.

Glossary

<<glossary: literate document>> literate document
A file or collection of files that include both source code and prose to explain it. Well-known formats include Noweb files (*.nw) and Org mode files (*.org).
<<glossary: monoblock>> monoblock
an Org mode source code block with a #+name: ... field. This block is an independent block and there are no other blocks with the same name.
<<glossary: Noweb>> Noweb
A literate programming tool from 1989 that still works and from which Org mode borrows heavily using Noweb-style references. See Wikipedia.
<<glossary: noweb-ref>> noweb-ref
aka “Noweb-style reference”. A Noweb-style reference is just a name (string) that refers to a monoblock or polyblock. See the Org manual.
<<glossary: Org mode>> Org mode
An Emacs major mode for *.org files, where “major mode” means that it provides things like syntax highlighting and keyboard shortcuts for *.org text files if you are using Emacs. For Lilac, the important thing is that we use Org mode as a literate programming tool. See Org mode.
<<glossary: polyblock>> polyblock
an Org mode source code block without a #+name: ... field, but which has a #+header: :noweb-ref ... field. Other blocks with the same Noweb-ref name are concatenated together when they are tangled. Polyblocks are used in cases where we would like to break up a single block into smaller pieces for explanatory purposes. In all other cases, monoblocks are preferable, unless the source code block is not to be tangled and is only for explanatory purposes in the woven output.
<<glossary: source code block>> source code block
An Org mode facility that allows you to enclose a multiline text (typically source code) with #+begin_src ... and #+end_src lines. They are enclosed in a separate background color in the HTML output, and are often used for illustrating source code listings. The format is #+begin_src LANGUAGE_NAME where LANGUAGE_NAME is the name of the programming language used for the listing. If the name is a recognized name, it will get syntax highlighting in the output automatically.
<<glossary: tangling>> tangling
The act of extracting source code from a raw literate document.
<<glossary: weaving>> weaving
The act of converting a raw literate document to a richer format such as PDF or HTML. This allows fancier output, such as for mathematical formulas, which are easier to read versus the original literate document.

Projects using Lilac

References