implement macros

This commit is contained in:
evilchili committed 2026-10-17 13:28:47 -07:00
1 parent a306820a78
commit 241f67fb4d
27 files changed
+597 -257

No files matched your search

+9 -94
View File
@@ -80,16 +80,6 @@ class Tokenizer {
continue;
}
}
// Hard line break: two+ trailing spaces before newline
if (source[position] === ' ') {
const spaceMatch = remaining.match(/^(?<spaces> {2,})\n/);
if (spaceMatch?.groups) {
flushText();
tokens.push({ role: 'block', value: '<br>' });
position += spaceMatch[0].length;
continue;
}
}
// HTML entity resolution: &name; or &#digits; or &#xhex;
if (source[position] === '&') {
const resolved = this.resolveEntity(remaining);
@@ -106,13 +96,16 @@ class Tokenizer {
position += match.value?.length;
continue;
}
// check for autolinks
const anchor = this.tags.getByElementName('A');
match = anchor.tokenize('block', remaining[0], remaining);
if (match && match.value?.length) {
flushText();
tokens.push(match);
position += match.value?.length;
continue;
if (anchor) {
match = anchor.tokenize('block', remaining[0], remaining);
if (match && match.value?.length) {
flushText();
tokens.push(match);
position += match.value?.length;
continue;
}
}
// Plain character
textBuffer += source[position];
@@ -155,84 +148,6 @@ class Tokenizer {
}
return null;
}
/**
* Match a code span starting at the given position.
* Handles single backtick delimiters only (not multi-backtick).
*/
matchCodeSpan(source, position) {
if (source[position] !== '`') {
return null;
}
const closeIndex = source.indexOf('`', position + 1);
if (closeIndex === -1) {
return null;
}
const content = source.slice(position + 1, closeIndex);
return {
content,
raw: source.slice(position, closeIndex + 1),
};
}
/**
* Match a markdown link [text](url) or [text](url "title")
* starting at the given position. Disallows [ in link text
* to prevent nested link ambiguity.
*/
matchLink(source, position) {
const linkPattern = /^\[(?<text>[^\[\]]+)\]\((?<href>[^\s)]+)(?:\s+"(?<title>[^"]*)")?\)/;
const match = source.slice(position).match(linkPattern);
if (!match?.groups) {
return null;
}
return {
text: match.groups.text,
href: match.groups.href,
title: match.groups.title,
length: match[0].length,
};
}
/**
* Match an angle-bracket autolink <url> at the start of the string.
*/
matchAutolink(text) {
const pattern = /^<(?<url>https?:\/\/[^\s>]+)>/;
const match = text.match(pattern);
if (!match?.groups) {
return null;
}
return {
url: match.groups.url,
length: match[0].length,
};
}
/**
* Match a bare URL (https://...) at the start of the string.
*/
matchBareUrl(text) {
const pattern = /^[\w]+?:\/\/[^\s<>\x00]+/;
const match = text.match(pattern);
if (!match) {
return null;
}
return {
url: match[0],
length: match[0].length,
};
}
/**
* Match an HTML tag at the start of the string.
*/
matchHtmlTag(text) {
const pattern = /^<\/?[a-zA-Z][a-zA-Z0-9]*(?:\s+[^>]*)?\s*\/?>/;
const match = text.match(pattern);
if (!match) {
return null;
}
return {
tag: match[0],
length: match[0].length,
};
}
/**
* Try to match a delimiter at the given position. For runs of the
* same character (e.g. *** = 3 asterisks), the run is split into