Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/mistune/plugins/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"ruby": "mistune.plugins.ruby.ruby",
"task_lists": "mistune.plugins.task_lists.task_lists",
"spoiler": "mistune.plugins.spoiler.spoiler",
"container": "mistune.plugins.container.plugin_container",
}


Expand Down
83 changes: 83 additions & 0 deletions src/mistune/plugins/container.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import re
from typing import TYPE_CHECKING, Match, Optional

if TYPE_CHECKING:
from ..block_parser import BlockParser
from ..core import BaseRenderer, BlockState
from ..markdown import Markdown

__all__ = ["plugin_container"]

CONTAINER_PATTERN = r"^ {0,3}:::(?:\s+([a-zA-Z0-9_-]+))?(?:\s+(.*?))?\n((?:(?!^ {0,3}:::).*?\n)*?)^ {0,3}:::\n*"


def parse_container(block: "BlockParser", m: Match[str], state: "BlockState") -> Optional[int]:
pos = m.end()

# 提取匹配的整个字符串
matched_text = m.group(0)

# 分割成行
lines = matched_text.split('\n')

# 处理第一行,提取类型和标题
first_line = lines[0].strip()
parts = first_line.split(' ', 2)

container_type = ""
title = ""

if len(parts) > 1:
container_type = parts[1]
if len(parts) > 2:
title = ' '.join(parts[2:])

# 提取内容(中间的行)
content_lines = lines[1:-2] # 排除第一行和最后两行(:::)
content = '\n'.join(content_lines)

# 总是添加 type 和 title 键到 attrs 中
attrs = {
"type": container_type,
"title": title
}

# 创建子状态来解析容器内部的内容
child = state.child_state(content)
# 解析子状态中的内容
block.parse(child)

# 添加容器令牌,使用 children 字段来存储子状态的令牌
# 这样 HTMLRenderer 就会正确处理容器内部的 Markdown 语法
state.append_token({
"type": "container",
"attrs": attrs,
"children": child.tokens
})
return pos


def render_html_container(renderer, text: str = "", **kwargs) -> str:
type = kwargs.get("type", "")
title = kwargs.get("title", "")

classes = ["container"]
if type:
classes.append(type)

html = f'<div class="{" ".join(classes)}">\n'
if title:
html += f" <p class='container-title'>{title}</p>\n"
if text:
html += text
html += "</div>\n"
return html


def plugin_container(md: "Markdown") -> None:
"""A mistune plugin to support custom containers."""
# 确保容器插件的优先级高于段落插件
md.block.register("container", CONTAINER_PATTERN, parse_container, before="paragraph")

if md.renderer and md.renderer.NAME == "html":
md.renderer.register("container", render_html_container)