153 lines
3.4 KiB
TypeScript
153 lines
3.4 KiB
TypeScript
import { zipArrays } from "../zip";
|
|
|
|
export const tokenizeInline = (line: string) => {
|
|
line = line.trim();
|
|
const originalLine = line;
|
|
const insertMarker = "\u{03A9}";
|
|
const tokens: InlineTokenInsert[] = [];
|
|
|
|
for (const token of inlineTokens) {
|
|
const rx = new RegExp(token.rx);
|
|
let match;
|
|
while ((match = rx.exec(line)) !== null) {
|
|
const tokenStart = match.index;
|
|
const tokenEnd = match.index + match[0].length;
|
|
|
|
const wrappingToken = tokens.find((t) =>
|
|
t.start < tokenStart && t.end > tokenStart
|
|
);
|
|
if (wrappingToken) continue;
|
|
|
|
let wrappedToken;
|
|
while (
|
|
(wrappedToken = tokens.findIndex((t) =>
|
|
t.start > tokenStart && t.start < tokenEnd
|
|
)) !== -1
|
|
) {
|
|
tokens.splice(wrappedToken, 1);
|
|
}
|
|
|
|
token.create(match, tokenStart, tokenEnd, tokens);
|
|
}
|
|
}
|
|
|
|
if (tokens.length) {
|
|
for (const insert of tokens) {
|
|
line = line.slice(0, insert.start) +
|
|
"".padStart(insert.end - insert.start, insertMarker) +
|
|
line.slice(insert.end, line.length);
|
|
}
|
|
|
|
return zipArrays(
|
|
line.split(new RegExp(insertMarker + "{2,}")).map((t): InlineToken => ({
|
|
content: t,
|
|
type: "text",
|
|
uuid: crypto.randomUUID(),
|
|
})),
|
|
tokens,
|
|
).filter((t) => t.content);
|
|
}
|
|
return originalLine;
|
|
};
|
|
|
|
const joiner = "<><>";
|
|
export const inlineTokens: {
|
|
rx: RegExp;
|
|
create: (
|
|
content: RegExpExecArray,
|
|
start: number,
|
|
end: number,
|
|
tokens: InlineTokenInsert[],
|
|
) => void;
|
|
replace: (line: string) => string;
|
|
}[] = [
|
|
{
|
|
rx: /(\*\*)(.*?)(\*\*)/g,
|
|
create(content, start, end, tokens) {
|
|
tokens.push({
|
|
content: this.replace(content[0]),
|
|
type: "bold",
|
|
end,
|
|
start,
|
|
uuid: crypto.randomUUID(),
|
|
});
|
|
},
|
|
replace(l) {
|
|
return l.replace(this.rx, (_, __, val) => val);
|
|
},
|
|
},
|
|
{
|
|
rx: /(?<!\*)\*([^\*]+?)\*(?!\*)/g,
|
|
create(content, start, end, tokens) {
|
|
tokens.push({
|
|
content: this.replace(content[0]),
|
|
type: "italic",
|
|
end,
|
|
start,
|
|
uuid: crypto.randomUUID(),
|
|
});
|
|
},
|
|
replace(l) {
|
|
return l.replace(this.rx, (...all) => all[1]);
|
|
},
|
|
},
|
|
{
|
|
rx: /(?<![\!\?|^])\[(.*?)\]\((.*?)\)/g,
|
|
create(content, start, end, tokens) {
|
|
const [_, label, href] = content;
|
|
tokens.push({
|
|
content: label,
|
|
type: "anchor",
|
|
data: {
|
|
href,
|
|
},
|
|
start,
|
|
end,
|
|
uuid: crypto.randomUUID(),
|
|
});
|
|
},
|
|
replace(l) {
|
|
return l.replace(this.rx, (_, label, href) => [label, href].join(joiner));
|
|
// return l
|
|
},
|
|
},
|
|
{
|
|
rx: /!\[(.*?)\]\((.*?)\)/g,
|
|
create(content, start, end, tokens) {
|
|
const [_, alt, src] = content;
|
|
tokens.push({
|
|
content: alt,
|
|
end,
|
|
start,
|
|
type: "image",
|
|
data: {
|
|
src,
|
|
},
|
|
uuid: crypto.randomUUID(),
|
|
});
|
|
},
|
|
replace(l) {
|
|
return l;
|
|
},
|
|
},
|
|
{
|
|
rx: /\^\[(.*?)\]<<(.*?)>>/gm,
|
|
create(content, start, end, tokens) {
|
|
const [_, text, popover] = content;
|
|
tokens.push({
|
|
content: text,
|
|
end,
|
|
start,
|
|
type: "popover",
|
|
data: {
|
|
popover: tokenizeInline(popover),
|
|
},
|
|
uuid: crypto.randomUUID(),
|
|
});
|
|
},
|
|
replace(l) {
|
|
return l;
|
|
},
|
|
},
|
|
];
|