// Copyright (C) 2026 Danilo M. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. .pragma library // Splits a message into prose and fenced code, so each code block can carry // its own copy button: Qt's markdown renders fences but gives no per-block // hook. A fence opens with ``` or ~~~ (three or more) after any indent, as // inside a list item, and closes with a run of the same character at least // as long. The fence's indent is stripped from its lines. A fence still open // at the end, which is normal while a reply streams, is a code segment with // open set. function split(text) { const out = []; const prose = []; let code = null; const flush = () => { const t = prose.join("\n").trim(); if (t) out.push({ kind: "prose", text: t }); prose.length = 0; }; for (const line of text.split("\n")) { if (code) { const close = /^\s*(`{3,}|~{3,})\s*$/.exec(line); if (close && close[1][0] === code.fence[0] && close[1].length >= code.fence.length) { out.push({ kind: "code", lang: code.lang, text: code.lines.join("\n"), open: false }); code = null; } else { code.lines.push(line.startsWith(code.indent) ? line.slice(code.indent.length) : line.trimStart()); } continue; } const open = /^(\s*)(`{3,}|~{3,})\s*([^`\s]*)/.exec(line); if (open) { flush(); code = { indent: open[1], fence: open[2], lang: open[3], lines: [] }; } else { prose.push(line); } } if (code) out.push({ kind: "code", lang: code.lang, text: code.lines.join("\n"), open: true }); else flush(); return out; }