Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions script.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def parse_text(text: str) -> str:
text,
)
# '&': newline
text = re.sub(r'(?<!`)&\s*', '<br>', text)
text = re.sub(r'(?<!`)&amp;\s*', '<br>', text)
# '%': close message ('%%' to close whole writer)
text = re.sub(
r'(?<!`)%',
Expand Down Expand Up @@ -230,6 +230,14 @@ def process_line(
data: Data,
resolve_references: bool = True,
) -> str:
# Escape dangerous HTML characters.
# This preserves strings like "THE LEGEND OF THIS WORLD.#<DELTARUNE.>"
line = re.sub(
r'(&|<)',
lambda matches: {'&': '&amp;', '<': '&lt;'}[matches[1]],
line,
)

# Highlight localized strings
line = re.sub(
r'([A-Za-z0-9_]+loc\((?:\d+, )?)"((?:[^"\\]|\\.)+)(", "[a-z0-9_-]+")\)', # noqa: E501
Expand Down Expand Up @@ -280,7 +288,7 @@ def process_line(
flags=re.IGNORECASE,
)

line = f"<code class='code-line language-gml'>{line}</code>"
line = f'<code>{line}</code>'

return line

Expand Down
61 changes: 61 additions & 0 deletions static/script-highlighter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
importScripts(
"/static/highlight/highlight.min.js",
"/static/highlight/gml.min.js"
);

// Process the innerHTML of `table.code`. See script.js.
// This code is brittle. If we overhaul the HTML structure it might break.
// It's also not very rigorous about matching tag names and classes.

/** @param {MessageEvent<string>} event */
onmessage = function (event) {
let newHTML = '';
let insideCode = false;
let preHighlightedDepth = 0;

for (const chunk of event.data.split(/(<[^>]+>)/)) {
if (chunk.startsWith("<")) {
// We need to highlight text inside <code> elements.
// We don't nest <code> elements.
if (chunk.startsWith("<code")) {
insideCode = true;
} else if (chunk === "</code>") {
insideCode = false;
}

// We don't want to highlight text inside <span class="skip-highlight">.
// We do nest <span> elements so we need to keep a count of how deep we are.
if (chunk.startsWith("<span") && chunk.includes("skip-highlight")) {
preHighlightedDepth = 1;
} else if (preHighlightedDepth !== 0) {
if (chunk.startsWith("<span")) {
preHighlightedDepth++;
} else if (chunk === "</span>") {
preHighlightedDepth--;
}
}

newHTML += chunk;
continue;
}

if (!insideCode || preHighlightedDepth !== 0) {
newHTML += chunk;
continue;
}

if (chunk.trim() === "") {
newHTML += chunk;
continue;
}

// highlight.js expects text input, not HTML input.
const decoded = chunk.replace(
/&amp;|&lt;|&gt;/g,
(char) => ({"&amp;": "&", "&lt;": "<", "&gt;": ">"})[char]
);
newHTML += hljs.highlight(decoded, { language: "gml" }).value;
}

postMessage(newHTML);
}
2 changes: 1 addition & 1 deletion static/script.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ pre:not(.funcCode), code, .hljs {
font-family: "JetBrains Mono";
}

.code .selected {
.code .selected, .code tr:has(:target) {
background-color: var(--selected-line-background-color);
}

Expand Down
72 changes: 28 additions & 44 deletions static/script.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
(function() {
window.addEventListener('DOMContentLoaded', function() {
'use strict';
function highlightHash() {
const selectedRow = document.querySelector('.code .selected');
Expand All @@ -24,48 +24,32 @@

highlightHash();

const elements = [
...document.getElementsByClassName("code-line"),
];

// Unfortunately a standard document tree walker doesn't work here - it misses quite a lot of nodes.
// ¯\_(ツ)_/¯

/** @param {Node} el */
const getTextNodes = (el) => {
const nodes = [];

for (const child of el.childNodes) {
if (
child.nodeType == Node.ELEMENT_NODE &&
child.classList.contains("highlighted")
) continue;

if (child.nodeType == Node.TEXT_NODE) {
nodes.push(child);
} else {
nodes.push(...getTextNodes(child));
}
}
window.addEventListener("hashchange", highlightHash);

return nodes;
// We want to apply syntax highlighting. We have two problems.
//
// First, we have annotations that we want to preserve and not highlight.
// So we can't just let highlight.js go to town, we have to be careful.
//
// Second, the natural approach of calling replaceWith() on text nodes
// is extremely slow for large pages like gml_GlobalScript_scr_text,
// locking up the browser for ten seconds or more. Firefox spends >80%
// of its runtime just calling replaceWith().
//
// So we process the HTML manually, as text, and set innerHTML a single
// time at the end. This also lets us use a web worker to highlight
// in the background without blocking the main thread. (You can't send
// DOM nodes to web workers.)
//
// See script-highlighter.js for the gory details.

const table = document.querySelector("table.code");
const worker = new Worker("/static/script-highlighter.js");
/** @param {MessageEvent<string>} event */
worker.onmessage = function (event) {
table.innerHTML = event.data;
highlightHash();
worker.terminate();
};

for (const el of elements) {
// Highlighting has to be done super carefully and manually like this, otherwise
// the annotations won't show up and get overwritten by highlight.js.

for (const node of getTextNodes(el)) {
if (node.textContent.trim() == "") continue;

const replacement = document.createElement("code");

replacement.classList.add("highlighted");
replacement.innerHTML = hljs.highlight(node.textContent, { language: "gml" }).value;

node.replaceWith(replacement);
}
}

window.addEventListener("hashchange", highlightHash);
})();
worker.postMessage(table.innerHTML);
});
4 changes: 2 additions & 2 deletions templates/highlight/alarm.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<span class="alarmC"><span class="alarmU">{{ before_alarm }}<a href="{{ script_name }}.html" class="alarm">{{ alarm_content }}</a><pre class="code-line inline-code">{{ content_rest }}</pre></span><!--
<span class="alarmC"><span class="alarmU">{{ before_alarm }}<a href="{{ script_name }}.html" class="alarm">{{ alarm_content }}</a><pre class="inline-code">{{ content_rest }}</pre></span><!--
-->{% if script_content %}<!--
--><span class="alarmA"></span><!--
--><pre class="alarmT language-gml"><!--
--><pre class="alarmT"><!--
--><strong><a href="{{ script_name }}.html">{{ script_name }}.gml</a></strong><!--
--><br><br><!--
-->{{ script_content | safe }}<!--
Expand Down
4 changes: 2 additions & 2 deletions templates/highlight/function.html
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<pre class="inline-code code-line">{{ before_function }}</pre><!--
<pre class="inline-code">{{ before_function }}</pre><!--
--><span class="funcC"><!--
--><a class="func {% if script_name == 'gml_GlobalScript_scr_debug' %}debug{% endif %}" href="{{ script_name }}.html">{{ function_name }}</a><!--
-->{% if script_content %}<!--
--><pre class='funcCode language-gml'><!--
--><pre class='funcCode'><!--
--><strong><a href='{{ script_name }}.html'>{{ function_name }}</a></strong><!--
--><br><br><!--
-->{{ script_content | safe }}<!--
Expand Down
2 changes: 1 addition & 1 deletion templates/highlight/text.html
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<div class="langtext"><span class="highlighted">{{ parsed_text | safe }}</span><div class="langvar">{{ before_var }}{{ variable }}{{ after_var }}</div></div>
<div class="langtext"><span class="skip-highlight">{{ parsed_text | safe }}</span><div class="langvar">{{ before_var }}{{ variable | safe }}{{ after_var }}</div></div>
2 changes: 2 additions & 0 deletions templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
<title>{{ game }} script viewer</title>
{% include 'partials/head.html' %}
<link rel="stylesheet" href="/static/index.css" />
<script src="/static/highlight/highlight.min.js"></script>
<script src="/static/highlight/gml.min.js"></script>
<script src="/static/search.js"></script>
</head>
<body>
Expand Down
2 changes: 0 additions & 2 deletions templates/partials/head.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,5 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="robots" content="noindex">
<link rel="stylesheet" href="/static/highlight/github-dark.min.css" />
<script src="/static/highlight/highlight.min.js" type="text/javascript"></script>
<script src="/static/highlight/gml.min.js" type="text/javascript"></script>
<link rel="stylesheet" href="/static/fonts.css" />
<link rel="stylesheet" href="/static/main.css" />
4 changes: 1 addition & 3 deletions templates/script_page.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ <h2>{{ script_name }}</h2>
</td>

<td>
<pre class="code-line language-gml">{{ line | safe }}</pre>
<pre>{{ line | safe }}</pre>
</td>
</tr>
{% endfor %}
Expand All @@ -37,7 +37,5 @@ <h2>{{ script_name }}</h2>
<p>{{ footer }}</p>
{% endif %}
</footer>

<script src="/static/script.js"></script>
</body>
</html>
Loading