Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/git/vole.git

Write over SSH with a registered key:

git clone ssh://git@0.0.0.0:2222/vole.git

A free and open-source forge that lives in your Git repo.

Code

392 commit(s) on main

Merge change request 01M70WES5ZZDJ6RP226WB11BBZ: Let people rename and remove their own passkeys keenan · 26m ago 4b8c9e1d
history blame raw 4.3 KiB
1/* Highlights one blob, off the main thread.
2 *
3 * Prism is string-only: given text and a grammar it returns HTML, so the whole
4 * job — tokenise, split into lines, send the lines back — runs here and a large
5 * file never blocks the page. The page keeps its server-rendered line table and
6 * swaps in the contents of each line cell, so line numbers and anchors are
7 * present before this file runs and stay put afterwards.
8 *
9 * The worker starts with Prism's core — markup, css, clike, javascript — and a
10 * manifest of every other language, not with every grammar Prism ships. A job
11 * that names a language this worker has not loaded imports that language's files
12 * from static/prism-languages/ first, once: a page pays for the grammars it
13 * shows, and the code browser rarely shows more than a few. The manifest entry
14 * also carries the components that only tweak another grammar (js-extras,
15 * php-extras, xml-doc), which is why a language already present can still have
16 * files to import.
17 *
18 * Prism's core detects a WorkerGlobalScope, but `manual` keeps it from trying to
19 * walk a document that does not exist and `disableWorkerMessageHandler` keeps it
20 * from installing its own JSON-string listener on top of ours. */
21
22self.Prism = { manual: true, disableWorkerMessageHandler: true };
23importScripts('prism.min.js', 'prism-manifest.js');
24
25const has = (object, key) => Object.prototype.hasOwnProperty.call(object, key);
26
27// imported is what this worker has already run. importScripts re-executes a
28// script it is given twice, and an extras component run twice would insert its
29// grammar into the same language again, so each file is imported at most once.
30const imported = new Set();
31
32// load imports a language's files in manifest order, dependencies first. A file
33// that will not load is not fatal and not remembered, so a later job tries it
34// again rather than inheriting this job's failure.
35function load(files) {
36 const missing = files.filter((name) => !imported.has(name));
37 if (!missing.length) return true;
38 try {
39 // importScripts takes URLs as arguments; an array would stringify to one
40 // comma-joined URL and fetch nothing.
41 importScripts(...missing.map((name) => 'prism-languages/' + name + '.js'));
42 } catch (err) {
43 return false;
44 }
45 missing.forEach((name) => imported.add(name));
46 return true;
47}
48
49// grammarFor returns the grammar for a language, loading it first if needed. An
50// unknown name and a grammar that will not load both answer nothing, which the
51// caller turns into the plain text the page was served with.
52function grammarFor(lang) {
53 const files = has(self.PRISM_LANGUAGES, lang) ? self.PRISM_LANGUAGES[lang] : null;
54 if (files && !load(files)) return null;
55 return has(self.Prism.languages, lang) ? self.Prism.languages[lang] : null;
56}
57
58// splitLines closes the spans that are open at a newline and reopens them on the
59// next line, so a multi-line comment or string keeps its class across lines
60// without one line's span swallowing the next. Prism escapes markup, so the only
61// tags in its output are its own spans.
62function splitLines(html) {
63 const lines = [];
64 const open = [];
65 let line = '';
66 const tag = /<span[^>]*>|<\/span>|[^<]+/g;
67 let match;
68 while ((match = tag.exec(html)) !== null) {
69 const part = match[0];
70 if (part.charAt(0) === '<') {
71 if (part.charAt(1) === '/') open.pop();
72 else open.push(part);
73 line += part;
74 continue;
75 }
76 const pieces = part.split('\n');
77 for (let i = 0; i < pieces.length; i++) {
78 if (i > 0) {
79 line += '</span>'.repeat(open.length);
80 lines.push(line);
81 line = open.join('');
82 }
83 line += pieces[i];
84 }
85 }
86 lines.push(line);
87 return lines;
88}
89
90self.onmessage = (event) => {
91 const job = event.data || {};
92 const grammar = typeof job.lang === 'string' ? grammarFor(job.lang) : null;
93 if (!grammar || typeof job.code !== 'string') {
94 // An unknown language is not an error: the page keeps the plain text it was
95 // served with.
96 self.postMessage({ id: job.id, lines: null });
97 return;
98 }
99 let lines = null;
100 try {
101 lines = splitLines(self.Prism.highlight(job.code, grammar, job.lang));
102 } catch (err) {
103 // A grammar that throws on this input must not take the page's highlighting
104 // down with it: answer plain, as if the language were unknown.
105 lines = null;
106 }
107 self.postMessage({ id: job.id, lines });
108};
109