arcmoon-highlight colors ArcMoon code: as HTML in Node.js or the browser, or live in an editor that also
shows errors. It reads code with ArcMoon's own lexer, so it sees it exactly the way ArcMoon does, including the
JavaScript in ${ }$ and the CSS in [style]. You don't need to install arcmoon.
What's in it
arcmoon-highlight:staticHighlight,tokenize,defaultTokens,highlightJs,decorateJs.arcmoon-highlight/diagnostics:diagnose.arcmoon-highlight/dynamic:attachHighlighter, the live editor (needs CodeMirror, see below).
staticHighlight(src, options)
Turns ArcMoon code into highlighted HTML. Every token becomes a <span> with an inline color from the built-in dark theme:
import { staticHighlight } from "arcmoon-highlight";
const html = staticHighlight(`[div = class: "hero"]
[h1]Hello[end]
[end]`);
document.querySelector("pre").innerHTML = html;- It never throws: half-written code is highlighted up to the problem, and the rest is plain text.
- Text is escaped, so the result is safe to put in
innerHTML. - JavaScript in
${ }$andruntime ${ }$, and CSS in[style],style:,css.and--props, get their own colors.
Choosing colors
Each token is drawn by the first of these that answers: onToken, then tokens[type], then other, then the built-in colors:
import { staticHighlight } from "arcmoon-highlight";
const src = "[b]Hi[end]";
// tokens: a style per token type
staticHighlight(src, {
tokens: {
IDENTIFIER: "#ffcc00", // a color
TEXT: { color: "#22c55e", bold: true }, // color, bold, italic
END_KEYWORD: null // plain text, no span
}
});
// … <span style="color:#ffcc00">b</span> … <span style="color:#22c55e;font-weight:bold">Hi</span> … end …
// render: your own HTML for a token type
staticHighlight(src, { tokens: { TEXT: { render: (value) => `<mark>${value}</mark>` } } });
// onToken: decide for any token, with the ones around it; undefined falls through
staticHighlight(src, {
onToken: ({ prev, current, next }) => (current.type === "IDENTIFIER" ? `<b class="tag">${current.value}</b>` : undefined)
});
// other: the style for every token type you didn't list in tokens
staticHighlight(src, { tokens: { IDENTIFIER: "#ffcc00" }, other: "#888888" });tokens: a style per token type. A style is a color ("#ffcc00"), an object ({ color, bold, italic }),{ render }for your own HTML, ornullfor plain text.onToken({ prev, current, next }): decide for any token, looking at its neighbours. Return HTML, orundefinedto let the next rule decide.other: one style for every token type not intokens.
Colors from your CSS
Inline colors can't follow light and dark themes. With classPrefix, every token gets a class instead, and your CSS colors it:
import { staticHighlight } from "arcmoon-highlight";
staticHighlight('[b = id: "x"]Hi ${ count }$[end]', { classPrefix: "arcm-" });
// <span class="arcm-open-bracket">[</span><span class="arcm-identifier">b</span>
// <span class="arcm-key">id</span><span class="arcm-colon">:</span> <span class="arcm-string">"x"</span> …
// <span class="arcm-logic"> <span class="arcm-js-name">count</span> </span> …/* one stylesheet, both themes */
:root {
--arcm-tag: #c2185b;
--arcm-key: #0369a1;
--arcm-string: #15803d;
}
@media (prefers-color-scheme: dark) {
:root { --arcm-tag: #ff7a8c; --arcm-key: #7cc4ff; --arcm-string: #8fdba8; }
}
.arcm-identifier { color: var(--arcm-tag); }
.arcm-key, .arcm-js-property { color: var(--arcm-key); }
.arcm-string, .arcm-js-string { color: var(--arcm-string); }
.arcm-comment { font-style: italic; opacity: 0.6; }
.arcm-error { text-decoration: underline wavy #f43f5e; }- ArcMoon tokens: the prefix plus the token type in lowercase, with dashes:
arcm-identifier,arcm-open-bracket,arcm-end-keyword. - JavaScript:
arcm-js-keyword,arcm-js-name,arcm-js-call,arcm-js-string,arcm-js-number,arcm-js-operatorand more. - CSS:
arcm-css-property,arcm-css-class,arcm-css-value,arcm-css-colorand more. - Errors:
arcm-error, plusarcm-error-arcmoon,arcm-error-javascriptorarcm-error-css.
This site colors all its code this way.
Token types
tokenize gives the tokens staticHighlight colors, and defaultTokens the built-in colors for each type:
import { tokenize, defaultTokens } from "arcmoon-highlight";
tokenize('[p = class: "x"]Hi[end]');
// [{ type: "OPEN_BRACKET", value: "[" }, { type: "IDENTIFIER", value: "p" },
// { type: "WHITESPACE", value: " " }, { type: "EQUAL", value: "=" }, …]
// the built-in dark colors: start from them and change a few
const tokens = { ...defaultTokens, IDENTIFIER: "#ffcc00" };- Markup:
OPEN_BRACKET,CLOSE_BRACKET,IDENTIFIER,END_KEYWORD,EXCLAMATION_MARK,IMPORT,SLOT,FOR_EACH,TEXT,ESCAPE,WHITESPACE. - Props:
KEY,EQUAL,COLON,COMMA,STRING,NUMBER,BOOLEAN,WORD. - Code:
RUNTIME_KEYWORD,LOGIC_OPEN,LOGIC,LOGIC_CLOSE. - Comments:
COMMENT,COMMENT_BLOCK. - CSS:
CSS_SELECTOR,CSS_CLASS,CSS_ID,CSS_PSEUDO,CSS_ATTRIBUTE,CSS_AT_RULE,CSS_PROPERTY,CSS_VARIABLE,CSS_VALUE,CSS_NUMBER,CSS_COLOR,CSS_FUNCTION,CSS_STRING,CSS_IMPORTANT,CSS_COMMENT,CSS_PUNCTUATION,CSS_TEXT.
JavaScript alone
highlightJs(code, { classPrefix }) colors plain JavaScript, the same way as inside ${ }$. decorateJs does the same for editors, as ranges instead of HTML.
import { highlightJs } from "arcmoon-highlight";
highlightJs('const n = add(1, "x");', { classPrefix: "arcm-" });
// <span class="arcm-js-keyword">const</span> <span class="arcm-js-name">n</span>
// <span class="arcm-js-operator">=</span> <span class="arcm-js-call">add</span>…
// <span class="arcm-js-number">1</span>… <span class="arcm-js-string">"x"</span>…Finding errors: diagnose(src, options)
Finds what is wrong in ArcMoon code, without running it, each at its exact spot:
- ArcMoon: syntax errors from ArcMoon's lexer and parser, and ArcMoon's rules for code, like
exportin runtime code or a Node.js module in the browser. - JavaScript: every
${ }$is parsed, like the compiler does. - CSS: braces, syntax and missing colons are errors; unknown properties and bad values are warnings. Values with
var(),env()orattr()aren't checked.
import { diagnose } from "arcmoon-highlight/diagnostics";
diagnose(`[p]\${ const = 1 }$[end]
[style] .a { colr: red } [end]
runtime \${ export const x = 1; }$`);
// [
// { line: 1, column: 13, text: "=", severity: "error", source: "JavaScript",
// message: "Unexpected token", from: 12, to: 13 },
// { line: 2, column: 14, text: "colr", severity: "warning", source: "CSS",
// message: "\"colr\" is not a CSS property (did you mean \"color\"?)", from: 37, to: 41 },
// { line: 3, column: 12, text: "export const x = 1;", severity: "error", source: "ArcMoon",
// message: "export is not allowed in runtime code", from: 66, to: 85 }
// ]
diagnose(src, { rules: false }); // only syntax errors, not ArcMoon's rules- Each problem has
lineandcolumn(from 1),from/to(offsets), the underlinedtext,severity("error"or"warning"),sourceandmessage. rules: falsechecks syntax only. Rules that need the whole project (likebundlein the config) are left to the language server.
Pass the list to staticHighlight as errors, and each problem gets a wavy underline, with its message as a tooltip:
import { staticHighlight } from "arcmoon-highlight";
import { diagnose } from "arcmoon-highlight/diagnostics";
const src = "[p]${ 1 + }$[end]";
staticHighlight(src, { errors: diagnose(src) });
// … <span style="text-decoration:underline wavy #f59e0b;…" title="JavaScript: the code ends too early">+</span> …
staticHighlight(src, { classPrefix: "arcm-", errors: diagnose(src) });
// … <span class="arcm-error arcm-error-javascript" title="JavaScript: the code ends too early">…</span> …Live editor: attachHighlighter(element, options)
A CodeMirror 6 editor that highlights as you type and shows errors with wavy lines and dots by the line numbers. Install CodeMirror next to it:
npm install @codemirror/view @codemirror/state @codemirror/commands @codemirror/lintimport { attachHighlighter } from "arcmoon-highlight/dynamic";
const editor = attachHighlighter(document.getElementById("editor"), {
classPrefix: "arcm-", // or tokens / other / onToken, like staticHighlight
caretColor: "#e8eaf0",
showLineNumbers: true,
showErrors: true, // wavy lines and dots in the gutter
rules: true, // also check ArcMoon's rules, not only syntax
onDiagnostics: (list) => {}, // after every check: the full list, like diagnose()
onError: (text) => {} // after every check: the first problem as text, or null
});
editor.setValue("[h1]Hello[end]");
editor.getValue(); // "[h1]Hello[end]"
editor.onUpdate((code) => save(code)); // after every change
editor.goTo({ from: 4, to: 9 }); // select, scroll there and focus
editor.view; // the CodeMirror EditorView, for anything else
editor.destroy(); // remove it- Colors: the same
classPrefix,tokens,otherandonTokenasstaticHighlight. - Errors are checked 250 ms after typing stops.
showErrors: falsestill checks and callsonDiagnostics/onError, but draws nothing. - Methods:
setValue,getValue,onUpdate,goTo(for example to jump to an error),destroy, andviewfor anything else CodeMirror can do.
From a CDN
Two ready bundles with a global ArcMoonHighlight. Nothing else to install:
<!-- highlighting only: staticHighlight, tokenize, highlightJs, … (8 KB gzipped) -->
<script src="https://cdn.jsdelivr.net/npm/arcmoon-highlight/dist/arcmoon-highlight.js"></script>
<!-- everything: also diagnose and the live editor, CodeMirror inside (202 KB gzipped) -->
<script src="https://cdn.jsdelivr.net/npm/arcmoon-highlight/dist/arcmoon-highlight.full.js"></script>
<script>
const { staticHighlight, diagnose, attachHighlighter } = ArcMoonHighlight;
</script>