/* 文件:markdown/footnote.ts — 支持 markdown 脚注的解析规则与渲染。 */ import { Tokens, MarkedExtension } from "marked"; import { Extension } from "./extension"; const refRule = /^\[\^([^\]]+)\]/; // 匹配 [^label] const defRule = /^ *\[\^([^\]]+)\]:/; // 匹配 [^label]: export class FootnoteRenderer extends Extension { allDefs: any[] = []; defCounter = 0; async prepare() { this.allDefs = []; this.defCounter = 0; } async postprocess(html: string) { if (this.allDefs.length == 0) { return html; } let body = ''; for (const def of this.allDefs) { const {label, content} = def; const html = await this.marked.parse(content); const id = `fn-${label}`; body += `
  • ${html}
  • `; } return html + `

      ${body}
    `; } markedExtension(): MarkedExtension { return { extensions: [ { name: 'FootnoteRef', level: 'inline', start(src) { const index = src.indexOf('[^'); return index > 0 ? index : -1; }, tokenizer: (src) => { const match = src.match(refRule); if (match) { return { type: 'FootnoteRef', raw: match[0], text: match[1], }; } }, renderer: (token: Tokens.Generic) => { this.defCounter += 1; const id = `fnref-${this.defCounter}`; return `${this.defCounter}`; } }, { name: 'FootnoteDef', level: 'block', tokenizer: (src) => { const match = src.match(defRule); if (match) { const label = match[1].trim(); const end = src.indexOf('\n'); const raw = end === -1 ? src: src.substring(0, end + 1); const content = raw.substring(match[0].length); this.allDefs.push({label, content}); return { type: 'FootnoteDef', raw: raw, text: content, }; } }, renderer: (token: Tokens.Generic) => { return ''; } } ] } } }