dingzy5320 downloadsWrite pseudocode as plain indented text and render it as a centred, line-numbered text block. No LaTeX, no images.
Write pseudocode as plain indented text in Obsidian and get a centred, line-numbered block with bold, coloured keywords, indentation guides and right-aligned comments. Rendering is pure text: no LaTeX, no images, no dependencies.
```algo
Binary Search
Input: sorted array $A[1..n]$, key $x$
Output: index $i$ with $A[i] = x$, or -1
lo <- 1, hi <- n
while lo <= hi do
mid <- floor((lo + hi) / 2)
if A[mid] = x then
return mid
else if A[mid] < x then
lo <- mid + 1 // search right half
else
hi <- mid - 1 // search left half
return -1
```
The source stays readable as plain text. Nothing below needs a backslash command.
Input: and Output: labels are optional, and an empty value or - hides the row.Require:, Ensure:, Data:, Result:, Parameters:, Returns:, Precondition:, Postcondition: or Globals:.Input: (or another label) simply has no title.```algo
Heap insert
Require: a min-heap $H$ of size $n$
Ensure: $H$ is a min-heap of size $n + 1$
Data: key $k$
H[n + 1] <- k
```
Each non-blank line is a numbered step, and indentation (tabs, 2 or 4 spaces) sets the nesting. A blank line leaves a small gap. A line of three or more dashes (---) is not a step: it splits the block into several algorithms, see Class blocks.
| You write | You get |
|---|---|
| A keyword (see Keywords) | Bold, in the colour of its kind. |
<- -> <-> <= >= != => <=> ... |
← → ↔ ≤ ≥ ≠ ⇒ ⇔ … |
42, 3.14 |
A number, coloured. |
// text |
A muted ▷ text comment. After a step it is aligned to the right edge of the block. On a line of its own it stays in place. |
$x_i$ |
Obsidian's built-in math. Use \$ for a literal dollar sign. |
Merge-Sort(A) |
A capitalised name followed by ( is shown in small caps. |
function merge_sort(A) |
The name after function or procedure is shown in small caps, and so are later calls such as merge_sort(B). |
"if not found" |
Text in double quotes is left alone: no bold keywords inside. Coloured. |
`x` |
Inline code. |
There are five lists of keywords, close to what the LaTeX algorithmic packages use. Every list can be edited under Settings → Plain Pseudocode → Keywords: add words, remove them, or replace them all, for example with keywords in your own language. Letter case does not matter.
| List | Default words | Where they count |
|---|---|---|
| Control flow | if then else elif elsif elseif for foreach forall while do repeat until loop switch case default otherwise try catch finally begin end endif endfor endwhile endloop endswitch endfunction endprocedure function procedure return break continue |
Anywhere in a step. |
| Loop words | to downto from in each all by step parallel |
Only on a line that starts with for, foreach or forall, so "add x to S" stays plain. Shown like control flow. |
| Statements | print read write input output call swap assert error throw raise yield goto exit halt |
Only as the first word of a step, so "the error is small" stays plain. |
| Operators | and or not xor mod |
Anywhere in a step. |
| Constants | true false nil null |
Anywhere in a step. |
A word that is assigned to is never a keyword, so end <- n and step = 2 keep their variables plain.
A series of algorithms that belong together, such as the operations of a priority queue, can be one block. Put --- on a line of its own between them. The text before the first --- is the head of the whole block.
```algo
Priority Queue
Data: max-heap $H[1..maxSize]$, size
---
SiftUp(i)
Input: tree, i
Output: -
while i > 1 and H[Parent(i)] < H[i]:
swap H[Parent(i)] and H[i]
i <- Parent(i)
---
Insert(p)
Input: tree, p
Output: -
if size = maxSize:
return ERROR
size <- size + 1
H[size] <- p
SiftUp(size)
```
^PriorityQueue
Data: or Globals:. Any other line there is shown as a short note. It has no Input and Output of its own, and it may be empty.--- is written exactly like a block of its own: title, input, output, steps.algo and a space at the start of a line to start the next algorithm. It writes --- and the title, input and output rows.Obsidian can link to any block that has an identifier. For a code block, write it on its own line right after the closing fence, followed by a blank line (or the end of the note):
```algo
SiftUp(i)
...
```
^SiftUp
Text goes on here.
Then [[Note#^SiftUp|SiftUp]] jumps to the block, hovering shows it, and ![[Note#^SiftUp]] embeds it in another note. It works the same for a class block, so the whole class has one link.
Without the blank line after it, Obsidian reads the ^id line as a paragraph of its own and the block cannot be linked.
You do not need to write the identifier by hand. With the cursor in a block, run Copy link to pseudocode block from the command palette. It:
SiftUp(i) gives ^SiftUp, Priority Queue gives ^PriorityQueue), and adds a number if another block of the note has it;^id line after the block, or reuses the one that is there;[[Note#^SiftUp|SiftUp]], to the clipboard. This is the only thing the plugin does with the clipboard: it writes that link and never reads what is there.An algorithm inside a class block cannot be linked on its own: Obsidian cannot see inside a code block. Link the class, or keep the algorithm in a block of its own.
Keywords are bold, as in LaTeX, and each kind of token also gets a colour. The colours are the ones your theme uses for code, so they fit light and dark themes alike. Turn Syntax colors off in the settings for the plain black-and-white look.
To pick your own colours, set these variables from a CSS snippet:
.algo-colors {
--algo-control: var(--color-blue); /* control flow and loop words */
--algo-statement: var(--color-cyan); /* print, swap, error, ... */
--algo-operator: var(--color-red); /* and, or, not, mod */
--algo-constant: var(--color-purple); /* true, false, nil */
--algo-number: var(--color-purple);
--algo-function: var(--color-yellow); /* function names */
--algo-string: var(--color-green); /* "text in quotes" */
--algo-comment: var(--text-muted);
}
algo and a space (or algo and Tab) at the start of a line in your note to create a block. The cursor waits on the empty title line. Enter or Tab then moves to Input:, to Output: and to the first step.algo block, Enter keeps the indentation. It adds a level after a line ending in do, then, else, repeat, loop, begin or :, and after a loop or branch header such as for i from 1 to n or while x > 0.for and a space (or for and Tab) at the start of a line to get for i from 1 to n: and an indented line below it, with i selected. Type over it, then press Tab for 1, again for n, and once more to move into the loop.algo and a space starts the next algorithm of a class block.mk to get $$ with the cursor between the dollars. Tab jumps out of the math.algo (two of them: in the note and inside a block), for and mk are the built-in snippets. You can change them and add your own.Snippets expand a short trigger into a longer line with values to fill in, as in LaTeX Suite. They run on every line of an algo block. Only a snippet with the n option runs in the rest of your note, and never inside another code block.
LaTeX Suite itself does nothing inside a code block, and an algo block is one. So the snippets you want there, mk and your math shortcuts included, are defined here.
Edit them under Settings → Plain Pseudocode → Snippet definitions. The text is a JSON list, with // comments and trailing commas allowed. A mistake is reported under the box, and your last working snippets stay active until it is fixed.
{ "trigger": "for ", "replacement": "for ${0:i} from ${1:1} to ${2:n}:\n\t$3", "options": "Ab" }
| Field | Meaning |
|---|---|
trigger |
The text to type. Case matters. |
replacement |
What it turns into. |
options |
Optional letters, in any order (see below). |
| Option | Meaning |
|---|---|
A |
Automatic: expands as soon as the trigger is typed. Without it, type the trigger and press Tab. Tab works for automatic snippets too, and there it does not need the space a trigger ends with. |
b |
Only at the beginning of a line (after the indentation). |
w |
Only as a whole word: len expands, the end of maxlen does not. |
r |
The trigger is a regular expression. |
m |
Only inside math, between two $. |
t |
Only outside math. |
n |
For the note: runs in the text around your blocks instead of inside them. |
Inside replacement:
| You write | Meaning |
|---|---|
$0, $1, $2 |
Tabstops. The cursor starts at the lowest number, and Tab moves to the next one. Shift+Tab moves back. After the last one, Tab jumps to the end of the snippet. A last tabstop that is empty ends the snippet as soon as the cursor gets there. |
${0:i} |
A tabstop with a default value. The value is selected, so typing replaces it and Tab keeps it. |
| the same number twice | Linked tabstops: they are selected together and you type into all of them at once. A bare $0 repeats the default of ${0:i}. |
\n |
A new line at the indentation of the current line. |
\t |
One more level of indentation. |
[[0]], [[1]] |
With r: the first and second capture group of the trigger. |
\\$ |
A literal dollar sign, where it would otherwise read as a tabstop. Math such as $n$ or $x_i$ needs no escaping: only $ followed by a digit or by { and a digit is a tabstop. |
Enter accepts the values that are left and starts the next line. If the snippet has already made that line, as for does for its loop body, Enter moves into it instead of adding another. On a header row, Enter moves to the next value instead, like Tab. A snippet can be expanded inside a tabstop of another one: when it is done, Tab carries on with the outer snippet. Editing somewhere else, moving the cursor away or Undo ends a snippet, and Tab is a normal Tab again.
When several triggers match, the one listed first wins. Undo right after an automatic expansion gives back the text you typed. Because this is JSON, a backslash in a regular expression is written twice: \\d for a digit.
Paste this over the definitions to try everything out.
[
// The default. "A" expands it without Tab, "b" keeps it to the start of a line.
// The trigger ends with a space, so "foreach", "forall" and "format" can still be typed.
// Result: for i from 1 to n: (i selected, Tab goes to 1, then n, then into the empty line below)
{ "trigger": "for ", "replacement": "for ${0:i} from ${1:1} to ${2:n}:\n\t$3", "options": "Ab" },
// The default that creates a block. "n" makes it run in the note, outside any code block.
// Enter or Tab moves from the title ($0) to the input, the output and the first step.
{ "trigger": "algo ", "replacement": "```algo\\n$0\\nInput: $1\\nOutput: $2\\n$3\\n```", "options": "Abn" },
// The same word inside a block starts the next algorithm of a class block (no "n" option).
{ "trigger": "algo ", "replacement": "---\\n$0\\nInput: $1\\nOutput: $2\\n$3", "options": "Ab" },
// Another default. "t" keeps it out of math, so $mk$ can still be typed.
// Result: $$ with the cursor between the dollars, then Tab to continue after the math.
{ "trigger": "mk", "replacement": "$${0}$ $1", "options": "At" },
// No "A": type "fore" and press Tab.
// \n starts a new line, \t indents it. $2 is an empty tabstop inside the loop body.
{ "trigger": "fore", "replacement": "for each ${0:x} in ${1:S} do\n\t$2", "options": "b" },
{ "trigger": "wh", "replacement": "while ${0:condition} do\n\t$1", "options": "b" },
// Several lines. The numbers set the order, not the position:
// the condition is filled in first, then both branches from top to bottom.
{ "trigger": "ife", "replacement": "if ${0:condition} then\n\t$1\nelse\n\t$2", "options": "b" },
{ "trigger": "fn", "replacement": "function ${0:Name}(${1:args})\n\t$2\n\treturn ${3:result}", "options": "b" },
// Linked tabstops: both $0 are typed at once.
// Result: i <- i + 1
{ "trigger": "inc", "replacement": "${0:i} <- $0 + ${1:1}", "options": "b" },
// "w": a whole word only. "len" + Tab expands, "maxlen" + Tab does not.
{ "trigger": "len", "replacement": "length(${0:A})", "options": "w" },
// Automatic, anywhere on the line. The replacement is math, and its dollars need no escaping.
{ "trigger": "ooo", "replacement": "$\\infty$", "options": "A" },
// "m": only inside math. Typing sr between two dollars gives ^2.
{ "trigger": "sr", "replacement": "^2", "options": "Am" },
{ "trigger": "leq", "replacement": "\\leq ", "options": "Am" },
// Inside math, a letter followed by a digit becomes a subscript: a1 gives a_1.
{ "trigger": "([A-Za-z])(\\d)", "replacement": "[[0]]_[[1]]", "options": "Arm" },
// "r": a regular expression. [[0]] is the first group in ( ).
// Typing i++ gives: i <- i + 1
{ "trigger": "(\\w+)\\+\\+", "replacement": "[[0]] <- [[0]] + 1", "options": "Ar" },
// Two groups. Type a12 and press Tab to get the math $a_{12}$.
{ "trigger": "([A-Za-z])(\\d+)", "replacement": "$[[0]]_{[[1]]}$", "options": "rw" },
// A literal dollar in front of a tabstop is written \\$.
// Result: cost $5
{ "trigger": "cost", "replacement": "cost \\$${0:5}", "options": "w" },
]
| Setting | Default | What it does |
|---|---|---|
| Alignment | Center | Puts the block on the left, in the centre or on the right of the note. |
| Font size | 100 | Percent of the note's text size. |
| Font | Text font | The theme's text font or its monospace font. |
| Line numbers | On | Numbers each step. |
| Indent guides | On | Vertical lines that mark each nesting level. |
| Syntax colors | On | Colours each kind of token. Off gives black and white. See Colours. |
| Comments | Aligned to the right | Or right after the step, as in earlier versions. |
| Comment marker | ▷ |
Shown in place of //. |
| Keywords | five lists | The words to show in bold. See Keywords. |
| Enable snippets | On | Turns all snippets off without deleting them. |
| Snippet definitions | algo, for and mk |
See Snippets. |
The command Copy link to pseudocode block has no setting and no default hotkey, see Linking to a block.
Changes apply at once to every block that is open. Every setting can be found with the search box of the Obsidian settings.
Needs Obsidian 1.13 or later. Search for Plain Pseudocode under Settings → Community plugins → Browse. To install manually, download main.js, manifest.json and styles.css from the latest release into <vault>/.obsidian/plugins/plain-pseudocode/ and enable the plugin.
Colours and fonts follow your theme. Override any .algo-* rule from a CSS snippet (see styles.css), or just the colour variables.