- How it works
- Installation
- Usage — Browser
- Usage — Node.js
- Usage — Markdown parsers
- Choosing a theme
- Token reference
- Preprocessor directives
- Code Walkthrough
- Troubleshooting
xojo.prism.js is a self-executing function that registers Prism.languages['xojo'] on the global Prism object. It must be loaded after prism.js.
The grammar uses ordered pattern matching — patterns earlier in the definition take priority. The order is intentional:
comment(greedy) —//and'consume their lines firststring(greedy) — double-quoted stringspreprocessor(greedy) —#tag/#pragmalines, withinsideto sub-highlight the directive namekeyword,operator-keyword,builtin,boolean,type— word-boundary patternsnumber,operator,punctuation
The grammar is not derived from vb — it is a fresh definition to avoid inheriting incorrect Visual Basic patterns.
Download xojo.prism.js and place it anywhere in your project.
npm install xojo-syntax-highlightPrism highlights all code[class^="language-"] elements on DOMContentLoaded automatically.
<!-- 1. Stylesheet -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-tomorrow.min.css">
<!-- 2. Prism core -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js"></script>
<!-- 3. Xojo grammar — must come after prism.js -->
<script src="./xojo.prism.js"></script><!-- 4. Mark up code — Prism handles the rest -->
<pre><code class="language-xojo">
Var greeting As String = "Hello, World!"
</code></pre>Prism's built-in themes do not style .token.meta. Add one CSS rule:
.token.meta,
.token.preprocessor { color: #e5c07b; } /* amber — adjust to taste */const el = document.querySelector('#myCode');
el.className = 'language-xojo';
el.textContent = sourceCode;
Prism.highlightElement(el);Or highlight a string directly:
const html = Prism.highlight(sourceCode, Prism.languages.xojo, 'xojo');
container.innerHTML = `<pre><code class="language-xojo">${html}</code></pre>`;If you want to control when highlighting runs, add data-manual to the <script> tag that loads Prism:
<script src="prism.min.js" data-manual></script>Then call Prism.highlightAll() or Prism.highlightElement(el) manually.
import Prism from 'prismjs';
import './xojo.prism.js'; // registers Prism.languages.xojo as a side effect
const html = Prism.highlight(sourceCode, Prism.languages.xojo, 'xojo');With CommonJS:
const Prism = require('prismjs');
require('./xojo.prism.js');
const html = Prism.highlight(sourceCode, Prism.languages.xojo, 'xojo');import MarkdownIt from 'markdown-it';
import Prism from 'prismjs';
import './xojo.prism.js';
const md = new MarkdownIt({
highlight(str, lang) {
if (lang && Prism.languages[lang]) {
return Prism.highlight(str, Prism.languages[lang], lang);
}
return '';
}
});Then write Markdown:
```xojo
Function Fibonacci(n As Integer) As Integer
If n <= 1 Then Return n
Return Fibonacci(n - 1) + Fibonacci(n - 2)
End Function
```Most static site generators that use Prism for highlighting accept custom language files via their plugin or asset pipeline. Point the pipeline to xojo.prism.js and add it after the core Prism bundle.
Any Prism theme works. Popular dark themes:
| Theme | CDN path |
|---|---|
| Tomorrow Night | themes/prism-tomorrow.min.css |
| Okaidia | themes/prism-okaidia.min.css |
| One Dark | themes/prism-one-dark.min.css |
| Night Owl | themes/prism-night-owl.min.css |
| VS Code Dark+ | themes/prism-vsc-dark-plus.min.css |
Popular light themes:
| Theme | CDN path |
|---|---|
| Default | themes/prism.min.css |
| Solarized Light | themes/prism-solarizedlight.min.css |
| GitHub | themes/prism-github.min.css |
Browse all at prismjs.com.
Reminder: Add
.token.meta { color: … }to your CSS — none of the built-in themes include it.
| CSS class | Xojo constructs |
|---|---|
.token.keyword |
Var Dim Sub Function Class Module Interface Enum If Then Else ElseIf End For Each Next While Do Loop Select Case Try Catch Finally Return Raise RaiseEvent New Inherits Implements Extends AddHandler RemoveHandler Public Private Protected Static Shared Override Virtual Final Abstract Property Event Delegate As ByRef ByVal … and also Self Super Me (via builtin alias) |
.token.operator |
And Or Not Xor Mod In Is IsA AddressOf WeakAddressOf and symbolic operators < > + - * / = <> … |
.token.boolean |
True False Nil |
.token.class-name |
Integer Int8–Int64 UInt8–UInt64 Single Double Boolean String Variant Object Color Ptr Auto CString WString |
.token.comment |
// text and ' text (to end of line) |
.token.string |
"double quoted" (no multi-line) |
.token.number |
42 3.14 1e6 &hFF00FF &b10101010 |
.token.meta (via .token.preprocessor) |
#pragma DisableBackgroundTasks · #tag Module, Name = Utils · #if · #else · #endif · #region · #endregion |
Inside a preprocessor token, the #directive word gets an additional .token.directive.keyword class.
Xojo has two categories of preprocessor lines:
| Directive | Purpose |
|---|---|
#pragma |
Compiler hints (e.g. DisableBackgroundTasks, NilObjectChecking False) |
#tag / #tag EndModule etc. |
IDE metadata blocks (project file format) |
#if / #elseif / #else / #endif |
Conditional compilation |
#region / #endregion |
Code folding markers |
The grammar matches the entire line as a single preprocessor meta token. Inside it:
#directive→.token.directive.keyword(the directive word itself)- Everything after → plain text within the
metacontext
This means words like Module in #tag Module, Name = Utils are not coloured as Xojo keywords — they are part of the preprocessor argument, not executable code.
Example output HTML:
<span class="token preprocessor meta">
<span class="token directive keyword">#pragma</span> DisableBackgroundTasks
</span>This section explains every part of xojo.prism.js in detail.
(function (Prism) {
Prism.languages['xojo'] = { ... };
}(Prism));xojo.prism.js is an immediately-invoked function expression (IIFE) that registers the grammar on the global Prism object as soon as the script is evaluated. No separate registration call is needed. Prism must exist before this file runs — always load prism.js first.
Prism processes the language object in key order, and the first pattern that matches wins. The ordering in xojo.prism.js is:
comment → string → preprocessor → keyword → operator-keyword
→ builtin → boolean → type → number → operator → punctuation
This ordering ensures:
- Comments consume
//and'before string or keyword patterns can match inside them - Strings consume
"..."before keyword patterns see the content inside quotes - Preprocessor lines consume the entire
#tag ...line before keyword patterns can colourModuleas a keyword
Any token with greedy: true tells Prism: do not re-tokenize this match. Without it, Prism would scan each matched region again against all subsequent patterns in the language object. This would cause Module inside // Module example (a comment) to also receive the .token.keyword class. greedy: true prevents this.
'comment': [
{ pattern: /\/\/.*/, greedy: true },
{ pattern: /'[^\r\n]*/, greedy: true },
],An array value means Prism tries both patterns in sequence. Key detail: '[^\r\n]* uses a character class instead of .* with the m flag — Prism 1.29+ silently ignores the flags property on pattern objects, so [^\r\n]* is the correct way to stop at a line boundary.
'preprocessor': {
pattern: /#(?:tag|pragma|if|elseif|else|endif|region|endregion)\b[^\r\n]*/i,
greedy: true,
alias: 'meta',
inside: {
'directive': { pattern: /^#\w+/, alias: 'keyword' },
},
},This is the most complex token. Each part:
| Part | Meaning |
|---|---|
| `/#(?:tag | ...)\b[^\r\n]*/i` |
greedy: true |
Consume the whole line — patterns later in the list cannot match inside it |
alias: 'meta' |
The whole token gets the additional CSS class .token.meta |
inside: { 'directive': ... } |
After matching, Prism runs inside patterns on the token's text |
/^#\w+/ |
Matches only the #directive word at the start of the token (^) |
alias: 'keyword' |
The directive word also gets .token.keyword |
Result for #pragma DisableBackgroundTasks:
<span class="token preprocessor meta">
<span class="token directive keyword">#pragma</span> DisableBackgroundTasks
</span>Module in #tag Module, Name = Utils is plain text inside the meta span — not a keyword token.
'type': {
pattern: /\b(?:Integer|String|Double|...)\b/i,
alias: 'class-name',
},Prism themes typically include a .token.class-name rule (e.g. cyan in Tomorrow Night) but do not always include .token.type. Using alias: 'class-name' ensures type names are coloured by most themes without any extra CSS.
Code block is not highlighted
- Confirm
xojo.prism.jsloads afterprism.js. If Prism is not yet loaded when the grammar file runs,Prismwill beundefined. - Check the browser console for errors.
- The
<code>element must haveclass="language-xojo".
#pragma / #tag lines look the same as plain text
The token is matched correctly but no theme styles .token.meta. Add to your CSS:
.token.meta,
.token.preprocessor { color: #e5c07b; }' apostrophe comment is not highlighted
Do not use flags: 'm' in pattern objects — it is silently ignored in Prism 1.29+. The grammar uses /'[^\r\n]*/ (no $, no m flag) which correctly matches to end of line in multiline code.
Module inside #tag Module, Name = X is coloured as a keyword
This would indicate the preprocessor pattern is not matching. Ensure the grammar file loaded without errors and that Prism.languages.xojo is defined before the page renders.
Hex/binary literals show as plain text
Verify the number pattern is present: /&[hH][0-9a-fA-F]+\b|&[bB][01]+\b|.../. The & character is part of the match — in HTML source, write &h inside <code> elements (or let your markdown parser escape it).
Strings spanning multiple lines are not coloured
Xojo strings cannot span lines, so the pattern /"[^"\n]*"/ is intentionally single-line only. Multi-line string content is a syntax error in Xojo.