/** * 列表收紧插件(tightenLists) * * v1 的列表永远是「紧凑」渲染:
  • 内容
  • 。 * 标准 markdown 中,列表项之间有空行(模板和用户数据里非常常见) * 会渲染成「松散」列表:
  • 内容

  • ,视觉上多出段落间距。 * * 本插件把列表项内的段落解包为裸 inline 内容,保持 v1 的紧凑结构。 * 同一列表项内的多个段落之间插入
    ,避免内容粘连。 * 引用块内的段落不受影响(markdown-it 的引用渲染更符合预期)。 */ function tightenListsRule(state: any) { const tokens = state.tokens let liDepth = 0 let quoteDepth = 0 const unwraps: Array<{ index: number; insertBreak: boolean }> = [] for (let i = 0; i < tokens.length; i++) { const type = tokens[i].type if (type === "list_item_open") { if (liDepth === 0) quoteDepth = 0 // 新的顶层列表项重置引用状态 liDepth++ } else if (type === "list_item_close") { liDepth-- } else if (type === "blockquote_open") { quoteDepth++ } else if (type === "blockquote_close") { quoteDepth-- } else if (liDepth > 0 && quoteDepth === 0 && type === "paragraph_open") { if (tokens[i + 2]?.type === "paragraph_close") { unwraps.push({ index: i, insertBreak: tokens[i - 1]?.type === "inline" }) } } } for (let k = unwraps.length - 1; k >= 0; k--) { const { index, insertBreak } = unwraps[k] const inline = tokens[index + 1] const replacement: any[] = [inline] if (insertBreak) { // 同一列表项内的第二个及之后的段落,用硬换行分隔 const br = new state.Token("hardbreak", "br", 0) replacement.unshift(br) } tokens.splice(index, 3, ...replacement) } } export function tightenListsPlugin(md: any) { md.core.ruler.after("block", "resume_tighten_lists", tightenListsRule) }