Consolidate markdown linkification, and add support for authors, blobs, and messages.

This commit is contained in:
Cory McWilliams 2024-04-04 00:18:39 +01:00
parent e50144bd34
commit 4c8d24c319
5 changed files with 68 additions and 158 deletions

View File

@ -1,5 +1,5 @@
{ {
"type": "tildefriends-app", "type": "tildefriends-app",
"emoji": "🐌", "emoji": "🐌",
"previous": "&Xs1X5TzLCk6KVr+5IDc80JAHYxJyoD10cXKBUYpFqWQ=.sha256" "previous": "&0WzpczDOhxawXBY8fAPMQWwOuF8bVRO+cMJJTxBpYOQ=.sha256"
} }

View File

@ -1,90 +1,94 @@
function textNode(text) { function textNode(text) {
const node = new commonmark.Node("text", undefined); const node = new commonmark.Node('text', undefined);
node.literal = text; node.literal = text;
return node; return node;
} }
function linkNode(text, link) { function linkNode(text, link) {
const linkNode = new commonmark.Node("link", undefined); const linkNode = new commonmark.Node('link', undefined);
linkNode.destination = `#q=${encodeURIComponent(link)}`; if (link.startsWith('#')) {
linkNode.appendChild(textNode(text)); linkNode.destination = `#q=${encodeURIComponent(link)}`;
return linkNode; } else {
linkNode.destination = link;
}
linkNode.appendChild(textNode(text));
return linkNode;
} }
function splitMatches(text, regexp) { function splitMatches(text, regexp) {
// Regexp must be sticky. // Regexp must be sticky.
regexp = new RegExp(regexp, "gm"); regexp = new RegExp(regexp, 'gm');
let i = 0; let i = 0;
const result = []; const result = [];
let match = regexp.exec(text); let match = regexp.exec(text);
while (match) { while (match) {
const matchText = match[0]; const matchText = match[0];
if (match.index > i) { if (match.index > i) {
result.push([text.substring(i, match.index), false]); result.push([text.substring(i, match.index), false]);
} }
result.push([matchText, true]); result.push([matchText, true]);
i = match.index + matchText.length; i = match.index + matchText.length;
match = regexp.exec(text); match = regexp.exec(text);
} }
if (i < text.length) { if (i < text.length) {
result.push([text.substring(i, text.length), false]); result.push([text.substring(i, text.length), false]);
} }
return result; return result;
} }
const regex = new RegExp("(?<!\\w)#[\\w-]+"); const regex = new RegExp('(?:https?://[^ ]+[^ .,])|(?:(?<!\\w)#[\\w-]+)|(?:@[A-Za-z0-9+/]+=.ed25519)|(?:[%&][A-Za-z0-9+/]+=.sha256)');
function split(textNodes) { function split(textNodes) {
const text = textNodes.map(n => n.literal).join(""); const text = textNodes.map((n) => n.literal).join('');
const parts = splitMatches(text, regex); const parts = splitMatches(text, regex);
return parts.map(part => { return parts.map((part) => {
if (part[1]) { if (part[1]) {
return linkNode(part[0], part[0]); return linkNode(part[0], part[0]);
} else { } else {
return textNode(part[0]); return textNode(part[0]);
} }
}); });
} }
export function transform(parsed) { export function transform(parsed) {
const walker = parsed.walker(); const walker = parsed.walker();
let event; let event;
let nodes = []; let nodes = [];
while ((event = walker.next())) { while ((event = walker.next())) {
const node = event.node; const node = event.node;
if (event.entering && node.type === "text") { if (event.entering && node.type === 'text') {
nodes.push(node); nodes.push(node);
} else { } else {
if (nodes.length > 0) { if (nodes.length > 0) {
split(nodes) split(nodes)
.reverse() .reverse()
.forEach(newNode => { .forEach((newNode) => {
nodes[0].insertAfter(newNode); nodes[0].insertAfter(newNode);
}); });
nodes.forEach(n => n.unlink()); nodes.forEach((n) => n.unlink());
nodes = []; nodes = [];
} }
} }
} }
if (nodes.length > 0) { if (nodes.length > 0) {
split(nodes) split(nodes)
.reverse() .reverse()
.forEach(newNode => { .forEach((newNode) => {
nodes[0].insertAfter(newNode); nodes[0].insertAfter(newNode);
}); });
nodes.forEach(n => n.unlink()); nodes.forEach((n) => n.unlink());
} }
return parsed; return parsed;
} }

View File

@ -1,91 +0,0 @@
function textNode(text) {
const node = new commonmark.Node("text", undefined);
node.literal = text;
return node;
}
function linkNode(text, url) {
const urlNode = new commonmark.Node("link", undefined);
urlNode.destination = url;
urlNode.appendChild(textNode(text));
return urlNode;
}
function splitMatches(text, regexp) {
// Regexp must be sticky.
regexp = new RegExp(regexp, "gm");
let i = 0;
const result = [];
let match = regexp.exec(text);
while (match) {
const matchText = match[0];
if (match.index > i) {
result.push([text.substring(i, match.index), false]);
}
result.push([matchText, true]);
i = match.index + matchText.length;
match = regexp.exec(text);
}
if (i < text.length) {
result.push([text.substring(i, text.length), false]);
}
return result;
}
const urlRegexp = new RegExp("https?://[^ ]+[^ .,]");
function splitURLs(textNodes) {
const text = textNodes.map(n => n.literal).join("");
const parts = splitMatches(text, urlRegexp);
return parts.map(part => {
if (part[1]) {
return linkNode(part[0], part[0]);
} else {
return textNode(part[0]);
}
});
}
export function transform(parsed) {
const walker = parsed.walker();
let event;
let nodes = [];
while ((event = walker.next())) {
const node = event.node;
if (event.entering && node.type === "text") {
nodes.push(node);
} else {
if (nodes.length > 0) {
splitURLs(nodes)
.reverse()
.forEach(newNode => {
nodes[0].insertAfter(newNode);
});
nodes.forEach(n => n.unlink());
nodes = [];
}
}
}
if (nodes.length > 0) {
splitURLs(nodes)
.reverse()
.forEach(newNode => {
nodes[0].insertAfter(newNode);
});
nodes.forEach(n => n.unlink());
}
return parsed;
}

View File

@ -17,7 +17,6 @@
</script> </script>
<script src="filesaver.min.js"></script> <script src="filesaver.min.js"></script>
<script src="commonmark.min.js"></script> <script src="commonmark.min.js"></script>
<script src="commonmark-linkify.js" type="module"></script>
<script src="commonmark-hashtag.js" type="module"></script> <script src="commonmark-hashtag.js" type="module"></script>
<script src="script.js" type="module"></script> <script src="script.js" type="module"></script>
</body> </body>

View File

@ -1,4 +1,3 @@
import * as linkify from './commonmark-linkify.js';
import * as hashtagify from './commonmark-hashtag.js'; import * as hashtagify from './commonmark-hashtag.js';
function image(node, entering) { function image(node, entering) {
@ -67,7 +66,6 @@ export function markdown(md) {
writer.image = image; writer.image = image;
let parsed = reader.parse(md || ''); let parsed = reader.parse(md || '');
parsed = hashtagify.transform(parsed); parsed = hashtagify.transform(parsed);
parsed = linkify.transform(parsed);
let walker = parsed.walker(); let walker = parsed.walker();
let event, node; let event, node;
while ((event = walker.next())) { while ((event = walker.next())) {