This is a package for writing pseudocode in Typst. It is named after the computer science pioneer Ada Lovelace and inspired by the pseudo package for LaTeX.
Pseudocode is not a programming language, it doesn't have strict syntax, so you should be able to write it however you need to in your specific situation. Lovelace lets you do exactly that.
Main features include:
- arbitrary keywords and syntax structures
- optional line numbering
- line labels
- lots of customisation with sensible defaults
- Getting started
- Lower level interface
- Line numbers
- Referencing lines
- Indentation guides
- Spacing
- Decorations
- Algorithm as figure
- Customisation overview
- Exported functions
Import the package using
#import "@preview/lovelace:0.3.0": *
The simplest usage is via pseudocode-list
which transforms a nested list
into pseudocode:
#pseudocode-list[
+ do something
+ do something else
+ *while* still something to do
+ do even more
+ *if* not done yet *then*
+ wait a bit
+ resume working
+ *else*
+ go home
+ *end*
+ *end*
]
resulting in:
As you can see, every list item becomes one line of code and nested lists become indented blocks. There are no special commands for common keywords and control structures, you just use whatever you like.
Maybe in your domain very uncommon structures make more sense? No problem!
#pseudocode-list[
+ *in parallel for each* $i = 1, ..., n$ *do*
+ fetch chunk of data $i$
+ *with probability* $exp(-epsilon_i slash k T)$ *do*
+ perform update
+ *end*
+ *end*
]
If you feel uncomfortable with abusing Typst's lists like we do here, you can
also use the pseudocode
function directly:
#pseudocode(
[do something],
[do something else],
[*while* still something to do],
indent(
[do even more],
[*if* not done yet *then*],
indent(
[wait a bit],
[resume working],
),
[*else*],
indent(
[go home],
),
[*end*],
),
[*end*],
)
This is equivalent to the first example.
Note that each line is given as one content argument and you indent a block by
using the indent
function.
This approach has the advantage that you do not rely on significant whitespace and code formatters can automatically correctly indent your Typst code.
Lovelace puts a number in front of each line by default.
If you want no numbers at all, you can set the line-numbering
option to
none
.
The initial example then looks like this:
#pseudocode-list(line-numbering: none)[
+ do something
+ do something else
+ *while* still something to do
+ do even more
+ *if* not done yet *then*
+ wait a bit
+ resume working
+ *else*
+ go home
+ *end*
+ *end*
]
(You can also pass this keyword argument to pseudocode
.)
If you do want line numbers in general but need to turn them off for specific
lines, you can use -
items instead of +
items in pseudocode-list
:
#pseudocode-list[
+ normal line with a number
- this line has no number
+ this one has a number again
]
It's easy to remember:
-
items usually produce unnumbered lists and +
items produce numbered lists!
When using the pseudocode
function, you can achieve the same using
no-number
:
#pseudocode(
[normal line with a number],
no-number[this line has no number],
[this one has a number again],
)
Other than none
, you can assign anything listed
here
to line-numbering
.
So maybe you happen to think about the Roman Empire a lot and want to reflect that in your pseudocode?
#set text(font: "Cinzel")
#pseudocode-list(line-numbering: "I:")[
+ explore European tribes
+ *while* not all tribes conquered
+ *for each* tribe *in* unconquered tribes
+ try to conquer tribe
+ *end*
+ *end*
]
You can reference an inividual line of a pseudocode by giving it a label.
Inside pseudocode-list
, you can use line-label
:
#pseudocode-list[
+ #line-label(<start>) do something
+ #line-label(<important>) do something important
+ go back to @start
]
The relevance of the step in @important cannot be overstated.
When using pseudocode
, you can use with-line-label
:
#pseudocode(
with-line-label(<start>)[do something],
with-line-label(<important>)[do something important],
[go back to @start],
)
The relevance of the step in @important cannot be overstated.
This has the same effect as the previous example.
The number shown in the reference uses the numbering scheme defined in the
line-numbering
option (see previous section).
By default, "Line"
is used as the supplement for referencing lines.
You can change that using the line-number-supplement
option to pseudocode
or pseudocode-list
.
By default, Lovelace puts a thin gray (gray + 1pt
) line to the left of each
indented block, which guides the reader in understanding the indentations, just
like a code editor would.
You can customise this using the stroke
option which takes any value that is
a valid Typst stroke.
You can especially set it to none
to have no indentation guides.
The example from the beginning becomes:
#pseudocode-list(stroke: none)[
+ do something
+ do something else
+ *while* still something to do
+ do even more
+ *if* not done yet *then*
+ wait a bit
+ resume working
+ *else*
+ go home
+ *end*
+ *end*
]
Some people prefer using the indentation guide to signal the end of a block
instead of writing something like "end" by having a small "hook" at the end.
To achieve that in Lovelace, you can make use of the hook
option and specify
how far a line should extend to the right from the indentation guide:
#pseudocode-list(hooks: .5em)[
+ do something
+ do something else
+ *while* still something to do
+ do even more
+ *if* not done yet *then*
+ wait a bit
+ resume working
+ *else*
+ go home
]
You can control how far indented lines are shifted right by the indentation
option.
To change the space between lines, use the line-gap
option.
#pseudocode-list(indentation: 3em, line-gap: 1.5em)[
+ do something
+ do something else
+ *while* still something to do
+ do even more
+ *if* not done yet *then*
+ wait a bit
+ resume working
+ *else*
+ go home
+ *end*
+ *end*
]
You can also add a title and/or a frame around your algorithm if you like:
Using the title
option, you can give your pseudocode a title (surprise!).
For example, to achieve
CLRS style,
you can do something like
#pseudocode-list(stroke: none, title: smallcaps[Fancy-Algorithm])[
+ do something
+ do something else
+ *while* still something to do
+ do even more
+ *if* not done yet *then*
+ wait a bit
+ resume working
+ *else*
+ go home
+ *end*
+ *end*
]
If you like wrapping your algorithm in elegant horizontal lines, you can do so
by setting the booktabs
option to true
.
#pseudocode-list(booktabs: true)[
+ do something
+ do something else
+ *while* still something to do
+ do even more
+ *if* not done yet *then*
+ wait a bit
+ resume working
+ *else*
+ go home
+ *end*
+ *end*
]
Together with the title
option, you can produce
#pseudocode-list(booktabs: true, title: [My cool title])[
+ do something
+ do something else
+ *while* still something to do
+ do even more
+ *if* not done yet *then*
+ wait a bit
+ resume working
+ *else*
+ go home
+ *end*
+ *end*
]
By default, the outer booktab strokes are black + 2pt
.
You can change that with the option booktabs-stroke
to any valid
Typst stroke.
The inner line will always have the same stroke as the outer ones, just with
half the thickness.
To make algorithms referencable and being able to float in the document,
you can use Typst's figure
function with a custom kind
.
#figure(
kind: "algorithm",
supplement: [Algorithm],
caption: [My cool algorithm],
pseudocode-list[
+ do something
+ do something else
+ *while* still something to do
+ do even more
+ *if* not done yet *then*
+ wait a bit
+ resume working
+ *else*
+ go home
+ *end*
+ *end*
]
)
If you want to have the algorithm counter inside the title instead (see previous
section), there is the option numbered-title
:
#figure(
kind: "algorithm",
supplement: [Algorithm],
pseudocode-list(booktabs: true, numbered-title: [My cool algorithm])[
+ do something
+ do something else
+ *while* still something to do
+ do even more
+ *if* not done yet *then*
+ wait a bit
+ resume working
+ *else*
+ go home
+ *end*
+ *end*
]
) <cool>
See @cool for details on how to do something cool.
Note that the numbered-title
option only makes sense when nesting your
pseudocode inside a figure with kind: "algorithm"
, otherwise it produces
undefined results.
Both pseudocode
and pseudocode-list
accept the following configuration
arguments:
option | type | default |
---|---|---|
line-numbering |
none or a numbering |
"1" |
line-number-supplement |
content | "Line" |
stroke |
stroke | 1pt + gray |
hooks |
length | 0pt |
indentation |
length | 1em |
line-gap |
length | .8em |
booktabs |
bool | false |
booktabs-stroke |
stroke | 2pt + black |
title |
content or none |
none |
numbered-title |
content or none |
none |
Until Typst supports user defined types, we can use the following trick when wanting to set own default values for these options. Say, you always want your algorithms to have colons after the line numbers, no indentation guides and, if present, blue booktabs. In this case, you would put the following at the top of your document:
#let my-lovelace-defaults = (
line-numbering: "1:",
stroke: none,
booktabs-stroke: 2pt + blue,
)
#let pseudocode = pseudocode.with(..my-lovelace-defaults)
#let pseudocode-list = pseudocode-list.with(..my-lovelace-defaults)
Lovelace exports the following functions:
pseudocode
: Typeset pseudocode with each line as an individual content argument, see here for details. Has these optional arguments.pseudocode-list
: Takes a standard Typst list and transforms it into a pseudocode. Has these optional arguments.indent
: Inside the argument list ofpseudocode
, useindent
to specify an indented block, see here for details.no-number
: Wrap an argument topseudocode
in this function to have the corresponding line be unnumbered, see here for details.with-line-label
: Use this function in thepseudocode
arguments to add a label to a specific line, see here for details.line-label
: When usingpseudocode-list
, you do not usewith-line-label
but insert a call toline-label
somewhere in a line to add a label, see here for details.