static/js/hirad.js 2.9 KiB raw
1
(function (hirad) {
2
//
3
// hirad - Radiance Syntax Highlighter
4
//
5
// Copyright (c) 2020-2025 Alexis Sellier
6
//
7
const selector = hirad || '.language-radiance';
8
const keywords = [
9
    'fn', 'pub', 'if', 'else', 'for', 'while', 'break', 'switch', 'match',
10
    'record', 'union', 'const', 'align', 'let', 'use', 'mod', 'case',
11
    'continue', 'return', 'true', 'false', 'loop', 'extern', 'panic',
12
    'device', 'register', 'catch', 'throw', 'throws',
13
    'at', 'mut', 'nil', 'undefined', 'static', 'in', 'is', 'where',
14
    'as', 'and', 'or', 'xor', 'not', 'try', 'atomic', 'select', 'trait',
15
    'instance', 'assert'
16
];
17
const types = ['bool', 'u8', 'u16', 'u32', 'u64', 'i8', 'i16', 'i32', 'i64', 'f32', 'void', 'opaque'];
18
19
// Syntax definition.
20
//
21
// The key becomes the class name of the span around the matched block of code.
22
const syntax = [
23
    ['comment', /(\/\/[^\n]*)/g],
24
    ['string' , /("(?:(?!").|\\.)*"|'[^']{1,2}')/g],
25
    ['number' , /\b(0x[0-9a-fA-F]+|0b[01]+|[0-9]+(?:\.[0-9]+)?)\b/g],
26
    ['ref'    , /(&|&'|\*)\b/g],
27
    ['delim'  , /(->|=>|\(|\)|\{|\}|\[|\])/g],
28
    ['builtin', /(@[a-zA-Z]+)/g],
29
    ['access' , /(\.|::)/g],
30
    ['op'     , /(=|!=|\.\.|\+|-|\*|\/|%|\?{1,2}|!{1,2}|>=?|<=?)/g],
31
    ['keyword', new RegExp('\\b(' + keywords.join('|') + ')\\b', 'g')],
32
    ['type'   , new RegExp('\\b(' + types.join('|') + ')\\b', 'g')],
33
];
34
35
const table = {};
36
37
// Encode ASCII characters to Braille to avoid conflicts between patterns.
38
const encode = (str) => {
39
    const encoded = [...str].map(c =>
40
        c.charCodeAt(0) <= 127 ? String.fromCharCode(c.charCodeAt(0) + 0x2800) : c
41
    ).join('');
42
    table[encoded] = str;
43
44
    return encoded;
45
};
46
47
// Decode Braille back to ASCII.
48
const decode = (str) =>
49
    table[str] || [...str].map(c => {
50
        const code = c.charCodeAt(0) - 0x2800;
51
        return code >= 0 && code <= 127 ? String.fromCharCode(code) : c;
52
    }).join('');
53
54
// Escape HTML special characters.
55
const escape = (str) =>
56
    str.replace(/</g, '&lt;').replace(/>/g, '&gt;');
57
58
// Highlight all matching elements.
59
for (const node of document.querySelectorAll(selector)) {
60
    for (const child of node.childNodes) {
61
        if (child.nodeType !== Node.TEXT_NODE) continue;
62
        if (/^\$\s/.test(child.nodeValue.trim())) continue; // Skip shell snippets.
63
64
        for (const [cls, re] of syntax) {
65
            child.nodeValue = child.nodeValue.replace(re, (_, m) =>
66
                '\u00ab' + encode(cls) + '\u00b7' +
67
                           encode(m)   +
68
                '\u00b7' + encode(cls) + '\u00bb'
69
            );
70
        }
71
    }
72
    node.innerHTML = node.innerHTML.replace(
73
        /\u00ab(.+?)\u00b7(.+?)\u00b7\1\u00bb/g,
74
        (_, name, value) => {
75
            value = value.replace(/\u00ab[^\u00b7]+\u00b7|\u00b7[^\u00bb]+\u00bb/g, '');
76
            return '<span class="' + decode(name) + '">'   +
77
                                     escape(decode(value)) +
78
                   '</span>';
79
        }
80
    );
81
}
82
83
})(window.hirad);