import { zipArrays } from "../zip"; import { tokenizeLine } from "./tokenizeLine"; import { tokenizeBlock } from "./tokenizeBlock"; import { tokenizeParagraph } from "./tokenizeParagraph"; export const createElements = (body: string) => { const tokens = tokenize(body); return tokens; }; const tokenize = (body: string) => { body = body.replace(/\n?\n?/gs, ""); const paragraphs = body.split("\n\n"); const blockTokens: BlockToken[] = []; const paragraphTokens: ParagraphToken[] = []; for (const paragraph of paragraphs) { const block = tokenizeBlock(paragraph); let openBT = blockTokens.findLast((bt) => !bt.closed); if (block) { if (typeof block === "string") { if (openBT) { openBT.closed = true; } continue; } if (openBT) { openBT.children.push(block); block.parent = openBT.type; } blockTokens.push(block); continue; } if (!openBT) { openBT = { children: [], closed: false, metadata: {}, type: "block", uuid: crypto.randomUUID(), }; blockTokens.push(openBT); } const multiline = tokenizeParagraph(paragraph); let openP = paragraphTokens.findLast((p) => !p.closed); if (multiline) { if (Array.isArray(multiline)) { if (openP) { openP.closed = true; openP.content = openP.content.concat(multiline); } continue; } openBT.children.push(multiline); paragraphTokens.push(multiline); continue; } else if (openP && !openP?.allowsInline) { openP.content.push({ line: paragraph, raw: paragraph, type: "text", uuid: crypto.randomUUID(), }); } // I don't think the closed check is necessary, but just in case // if (openP && !openP.closed && !openP.allowsInline) continue; if (!openP) { openP = { allowsInline: true, closed: true, content: [], metadata: {}, type: "p", uuid: crypto.randomUUID(), }; openBT.children.push(openP); paragraphTokens.push(openP); } const lines = paragraph.split("\n"); let previous; for (const line of lines) { const singleLine = tokenizeLine(line, previous); if (singleLine) { if (singleLine !== previous) { openP.content.push(singleLine); } previous = singleLine; } } } return blockTokens.filter((b) => !b.parent); };