import type { BlockHandler, IdentifyResult } from "./handler.type.js"; import { ITEM_SEPARATOR, indentWidth, isBlockBoundary, leavesParagraphOpen } from "./identify.js"; import { isLooseList, renderItemContent, serializeItemTail, splitItemHead } from "./list-item.js"; /** Pattern matching the start of a task list item: `- [ ] `, `- [x] `, etc. */ const TASK_ITEM_RE = /^[-*+] \[([ xX])\] /; export const taskListHandler = { identify(lines: string[], index: number): IdentifyResult | null { const first = lines[index].trimStart(); if (!TASK_ITEM_RE.test(first)) return null; const items: string[] = []; let currentItemLines: string[] = []; let column = 0; let i = index; while (i < lines.length) { const cur = lines[i]; if (cur.trim() === "") { // Blank line — continue if next line is another task item or indented if (i + 1 < lines.length) { const nextTrimmed = lines[i + 1].trimStart(); const nextIsTask = TASK_ITEM_RE.test(nextTrimmed); const nextInItem = currentItemLines.length > 0 && indentWidth(lines[i + 1]) >= column; if (nextIsTask || nextInItem) { currentItemLines.push(""); i++; continue; } } break; } if (currentItemLines.length > 0 && indentWidth(cur) >= column) { currentItemLines.push(cur); i++; continue; } const trimmed = cur.trimStart(); if (TASK_ITEM_RE.test(trimmed)) { if (currentItemLines.length > 0) { items.push(currentItemLines.join("\n")); } // Keep the checkbox prefix (e.g. "[ ] text" or "[x] text") currentItemLines = [trimmed.replace(/^[-*+] /, "")]; column = indentWidth(cur) + 2; i++; } else if ( currentItemLines.length > 0 && !isBlockBoundary(cur) && leavesParagraphOpen(currentItemLines[currentItemLines.length - 1]) ) { currentItemLines.push(cur); i++; } else { break; } } if (currentItemLines.length > 0) { items.push(currentItemLines.join("\n")); } if (items.length === 0) return null; return { raw: items.join(ITEM_SEPARATOR), nextIndex: i, }; }, render(raw: string): string { const items = raw.split(ITEM_SEPARATOR); const loose = isLooseList(items); const inner = items .map((itemRaw) => { const checked = /^\[x\] /i.test(itemRaw); const text = itemRaw.replace(/^\[([ xX])\] /, ""); const checkedAttr = checked ? " checked" : ""; return `