Search...Search plugins and themes...
⌘K
Sign in
  • Get started
  • Download
  • Pricing
  • Enterprise
  • Account
  • Obsidian
  • Overview
  • Sync
  • Publish
  • Canvas
  • Mobile
  • Web Clipper
  • CLI
  • Learn
  • Help
  • Developers
  • Changelog
  • About
  • Roadmap
  • Blog
  • Resources
  • System status
  • License overview
  • Terms of service
  • Privacy policy
  • Security
  • Community
  • Plugins
  • Themes
  • Discord
  • Forum / 中文论坛
  • Merch store
  • Brand guidelines
Follow us
DiscordTwitterBlueskyThreadsMastodonYouTubeGitHub
© 2026 Obsidian

Plain Pseudocode

dingzy53dingzy5320 downloads

Write pseudocode as plain indented text and render it as a centred, line-numbered text block. No LaTeX, no images.

Add to Obsidian
  • Overview
  • Scorecard
  • Updates4

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
```

Syntax

The source stays readable as plain text. Nothing below needs a backslash command.

Header

  • Line 1 is the title, line 2 the input and line 3 the output. The Input: and Output: labels are optional, and an empty value or - hides the row.
  • Write another label to have it shown instead: Require:, Ensure:, Data:, Result:, Parameters:, Returns:, Precondition:, Postcondition: or Globals:.
  • More labelled rows may follow line 3. The first line without one of these labels is step 1.
  • Line 1 may stay empty: a block that starts with 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
```

Steps

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.

Keywords

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.

Class blocks

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
  • The head is the title on its first line, then labelled rows such as 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.
  • Each algorithm after a --- is written exactly like a block of its own: title, input, output, steps.
  • It is one box, so every algorithm has the same width, the step text starts in the same column and comments end at the same right edge.
  • Step numbers start again at 1 in every algorithm.
  • A function declared in one algorithm is styled where the others call it.
  • Inside a block, type algo and a space at the start of a line to start the next algorithm. It writes --- and the title, input and output rows.

Linking to a block

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:

  • takes the identifier from the title (SiftUp(i) gives ^SiftUp, Priority Queue gives ^PriorityQueue), and adds a number if another block of the note has it;
  • adds the ^id line after the block, or reuses the one that is there;
  • copies the link, for example [[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.

Colours

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);
}

Quick input

  • Type 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.
  • Or run Insert pseudocode block from the command palette (bind it to a hotkey under Settings → Hotkeys). It inserts the same block, with the cursor on the empty title line.
  • Inside an 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.
  • Type 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.
  • Inside a block, algo and a space starts the next algorithm of a class block.
  • Type 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

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.

One snippet

{ "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.

A complete example

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" },
]

Settings

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.

Install

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.

Styling

Colours and fonts follow your theme. Override any .algo-* rule from a CSS snippet (see styles.css), or just the colour variables.

License

MIT

HealthExcellent
ReviewPassed
About
Write pseudocode as plain indented text and render a centered, line-numbered block with bold keywords and visible indentation guides. Keep output as plain text with symbol conversions (←, →, ≤, ≥, ≠), muted // comments, Obsidian math support, and small-caps for capitalised function names.
SyntaxCodeFormatting
Details
Current version
0.1.3
Last updated
19 hours ago
Created
5 days ago
Updates
4 releases
Downloads
20
Compatible with
Obsidian 1.13.0+
Platforms
Desktop, Mobile
License
MIT
Report bugRequest featureReport plugin
Author
dingzy53dingzy53dingzy53
GitHubdingzy53
  1. Community
  2. Plugins
  3. Syntax
  4. Plain Pseudocode

Related plugins

Fountain Editor

Fountain (screenplay) syntax highlighting in the editor.

VSCode Editor

Edit Code Files like VSCode.

Shiki Highlighter

Highlight code blocks with Shiki.

Code Suite

Execute code inside your notes: Shiki syntax highlighting with 65+ themes, live streaming output, inline Matplotlib and Plotly graphs, shared variables, and styled HTML and PDF export with outputs.

Admonition

Admonition block-styled content.

Templater

Create and use dynamic templates.

Linter

Format and style your notes. Linter can be used to format YAML tags, aliases, arrays, and metadata; footnotes; headings; spacing; math blocks; regular Markdown contents like list, italics, and bold styles; and more with the use of custom rule options.

Mermaid Tools

Improved Mermaid.js experience: visual toolbar with common elements and more.

Editing Toolbar

The Editing Toolbar is modified from cMenu, which provides more powerful customization settings and has many built-in editing commands to be a MS Word-like toolbar editing experience.

Image Captions

Add captions to images with inline Markdown and link support. The caption format is compatible with the Commonmark spec and other Markdown applications.