Python Syntax Highlighting

This page is a live test bench for the code-block rendering used across the whole site. Every snippet below is plain HTML inside <pre><code class="language-xxx">; the colours, tokens and the Copy button are added in the browser by Prism.js and the shared copy-code.js script. The sections after the samples explain how that pipeline works, how to add a new language, and the mistakes that most often break highlighting.

Sample code blocks

Python, SQL, Bash and Go samples follow. If any of them render as plain grey text, the matching prism-<lang>.min.js component is missing from the page head.


def greet(name):
    print(f"Hello, {name}!")

def add(a, b):
    return a + b

if __name__ == "__main__":
    greet("World")
    result = add(5, 3)
    print(f"5 + 3 = {result}")

SQL Syntax Highlighting


SELECT name, age 
FROM users 
WHERE age > 18 
ORDER BY age DESC;

Bash Syntax Highlighting


#!/bin/bash
echo "Hello, World!"
mkdir new_directory
cd new_directory

Golang Syntax Highlighting


package main

import "fmt"

func main() {
    fmt.Println("Hello, World!")
}

func add(a int, b int) int {
    return a + b
}

1. What syntax highlighting does here

Syntax highlighting is purely presentational: it splits source text into lexical categories such as keywords, strings, comments, numbers and operators, then colours each category so the reader can parse structure at a glance. It does not execute, validate or compile the code. On this site, highlighting is performed client-side by Prism.js, a small library whose core is a few kilobytes and whose languages ship as separate grammar files. Each page loads the core plus the grammars it needs, and every <pre> block also receives a Copy button from the shared /static/copy-code.js script, so readers can paste examples straight into a terminal or notebook.

Because highlighting happens in the browser, the stored HTML stays clean and diff-friendly. The source of each example is exactly the text a reader copies; there are no colour spans in the file that could leak into the clipboard.

2. How Prism.js tokenizes a block

Page Load to Highlighted Code HTML <pre><code>class=language-sql prism.min.jscore loads Language grammarprism-sql.min.js Tokenizerregex grammar walk <span class=token>wrapped tokens Theme CSScolours applied Prism runs on DOMContentLoaded and highlights every element matching code[class*="language-"]. The HTML you write never contains colour; colour comes from the theme stylesheet.

When the page finishes parsing, Prism scans the document for code elements whose class contains language-. For each one it looks up the grammar registered under that name, for example Prism.languages.sql. A grammar is an ordered object of regular expressions. Prism walks the text, and for each token type in order it tries its pattern against the remaining unclaimed text. Matched slices become token objects; unmatched slices stay as plain strings. Nested grammars (for example interpolation inside a Python f-string) are handled by recursing into a token's inside grammar.

The token tree is then serialised back to HTML as <span class="token keyword"> and similar elements, and the result replaces the original text content of the code element. The theme stylesheet maps each .token.* class to a colour. Swapping themes therefore changes only CSS, never the page markup.

Grammar Matching Order (first match wins) comment# ... / -- ... stringquoted text keywordSELECT, def, func number42, 3.14 operator+ - = < punctuation( ) , ; Tokens are tried in grammar order; once text is claimed by an earlier token it is not re-matched. That is why a # inside a string stays a string: the string pattern consumed it first.

Order matters. Comments and strings are listed before keywords in almost every grammar so that a word like SELECT inside a quoted string, or a # inside a string literal, is not mis-coloured. When highlighting looks wrong, the cause is usually that a construct the grammar does not know about (a heredoc, a raw string, a dollar-quoted SQL body) let a later pattern claim text it should not have.

3. Markup contract for a code block

Every block on the site follows one shape: an outer <pre>, an inner <code> carrying the language class, and HTML-escaped content. The escaping is not optional. A literal < in a Bash redirect or a Go generic must be written as &lt;, otherwise the browser parses it as a tag before Prism ever sees it, and part of the example silently disappears.

LanguageClassGrammar file
Pythonlanguage-pythonprism-python.min.js
SQLlanguage-sqlprism-sql.min.js
Bash / shelllanguage-bashprism-bash.min.js
Golanguage-goprism-go.min.js
YAMLlanguage-yamlprism-yaml.min.js
Java / Scala / C / C++language-java etc.one file each

4. Worked example: adding a language

Suppose a page needs a JSON example. The core is already present, so only the grammar file and the class are required. Add the component script after prism.min.js in the head (components depend on the core and some depend on others; C++ requires C, for instance), then mark the block.

# verify which Prism components a page loads
grep -o 'prism-[a-z]*\.min\.js' page.html | sort -u

# find code blocks whose language has no matching component
python3 - <<'PY'
import re, sys
html = open("page.html").read()
used = set(re.findall(r'class="language-([a-z]+)"', html))
loaded = set(re.findall(r'prism-([a-z]+)\.min\.js', html))
print("missing grammars:", sorted(used - loaded - {"none"}))
PY

The script compares the languages referenced by blocks with the grammars loaded in the head and prints any gap. A block whose language is missing still renders, just without colour, so this check catches problems that a quick visual scan can miss on a long page.

5. Trade-offs: client-side vs build-time

ApproachStrengthsCosts
Client-side (Prism, highlight.js)Clean source HTML, no build step, theme swap is CSS onlyJS must load; brief unstyled flash; per-page grammar list to maintain
Build-time (Pygments, Shiki)Zero runtime JS, works with scripts disabled, very accurate grammarsNeeds a build pipeline; colour spans baked into HTML
None (plain pre)Simplest, smallestHarder to read long examples

For a hand-edited static site with no build step, client-side highlighting is the pragmatic choice. A site generated by a static-site builder would usually prefer build-time highlighting to remove the JavaScript dependency.

6. Pitfalls