A pure MoonBit Markdown to HTML conversion library.
| 语法 | 示例 | 优先级 |
|---|---|---|
| 标题 | # H1 ~ ###### H6 | P0 |
| 段落 | 连续文本块 | P0 |
| 换行 | 行尾双空格 / 空行分段 | P0 |
| 强调 | *italic* **bold** | P0 |
| 代码 | 行内 `code` 、代码块(缩进/围栏 ``` ) | P0 |
| 链接 | [text](url) [text](url "title") | P0 |
| 图片 |  | P0 |
| 无序列表 | - item / * item / + item | P0 |
| 有序列表 | 1. item | P0 |
| 块引用 | > quote(支持嵌套) | P0 |
| 水平线 | --- *** ___ | P0 |
| 转义 | \* \[ 等反斜杠转义 | P0 |
| HTML 原样输出 | 内联 <span>、块级 <div> | P0 |
| 语法 | 示例 | 优先级 |
|---|---|---|
| 表格 | 管道符表格 \| A \| B \| | P1 |
| 任务列表 | - [x] done - [ ] todo | P1 |
| 删除线 | ~~strikethrough~~ | P1 |
| 围栏代码块语言标注 | ```rust | P1 |
| 自动链接 | https://example.com | P1 |
| 功能 | 说明 | 优先级 |
|---|---|---|
| 语法高亮 | 代码块输出带 <span class="hl-*"> 包裹 | P2 |
| TOC 生成 | 根据标题生成目录结构 | P2 |
| 元数据解析 | 支持 Frontmatter (---\ntitle: ...\n---) | P2 |
| 脚注 | [^1] 风格脚注 | P2 |
| 自定义容器 | ::: tip / ::: warning | P2 |
import {
"moonbit-texteditor/src" @md,
}moon add moonbit-texteditorgit clone https://github.com/lexchb/moonbit-Texteditor.git
cd moonbit-Texteditor
moon build # 构建原生目标
moon build --target js # 构建 JS 目标(浏览器使用)moon run cmd/main# Python
python -m http.server 8000
# Node.js
npx serve .
# 然后访问 http://localhost:8000/demo.htmllet html = @md.render_to_string("# Hello World")
// => "<h1>Hello World</h1>"let opts = @md.create_markdown_options(
true, // enable_gfm_tables
true, // enable_gfm_tasklist
true, // enable_gfm_strikethrough
true, // enable_gfm_autolink
true, // enable_syntax_highlight
false, // hard_wrap
)
let html = @md.render_to_string_with_opts("~~text~~", opts)
// => "<p><del>text</del></p>"// 第一步:解析 Markdown 为 AST
let blocks = @md.parse("## Title\n\nSome **bold** text")
// 第二步:手动检查和操作 AST
match blocks[0] {
Heading(level, inlines) => println("Heading level: " + level.to_string())
_ => ()
}
// 第三步:渲染为 HTML
let html = @md.render(blocks)let input = "---\ntitle: My Doc\nauthor: Alice\n---\n\n# Content"
let (metadata, body) = @md.parse_frontmatter(input)
// metadata = [("title", "My Doc"), ("author", "Alice")]
// body = "# Content"let blocks = @md.parse("# A\n\n## B\n\n### C")
let toc = @md.generate_toc(blocks)
// toc = [(1, "A"), (2, "B"), (3, "C")]let highlighted = @md.highlight_code("fn main() {}", "rust")
// => "<span class=\"hl-keyword\">fn</span> main() {}"fn main {
let input = "# Markdown 示例\n\n" +
"这是一个包含 **加粗** 和 *斜体* 的段落。\n\n" +
"| 表格 | 列2 |\n" +
"|------|-----|\n" +
"| 单元格1 | 单元格2 |\n\n" +
"- [x] 已完成任务\n" +
"- [ ] 未完成任务\n"
// 创建选项:启用表格、任务列表、删除线
let opts = @md.create_markdown_options(
true, true, true, true, true, false,
)
let html = @md.render_to_string_with_opts(input, opts)
println(html)
}| 函数 | 参数 | 返回值 | 说明 |
|---|---|---|---|
| parse(input) | String | Array[Block] | 解析为 AST |
| parse_with_opts(input, opts) | String, MarkdownOptions | Array[Block] | 带选项解析 |
| render(blocks) | Array[Block] | String | AST → HTML |
| render_to_string(input) | String | String | 一站式转换(默认选项) |
| render_to_string_with_opts(input, opts) | String, MarkdownOptions | String | 带选项一站式转换 |
| render_html_with_opts(blocks, opts) | Array[Block], MarkdownOptions | String | 带选项渲染 |
| parse_frontmatter(input) | String | (Array[(String,String)], String) | 解析 Frontmatter |
| generate_toc(blocks) | Array[Block] | Array[(Int, String)] | 生成目录(标题层级, 标题文本) |
| create_markdown_options(...) | 6 × Bool | MarkdownOptions | 创建解析选项 |
| default_options() | — | MarkdownOptions | 默认选项(全部关闭) |
| highlight_code(code, lang) | String, String | String | 代码语法高亮 |
| escape_html(s) | String | String | HTML 转义 |
┌─────────────────────────────────────────────────────┐
│ 用户 API │
│ parse() / render() / render_to_string() │
├─────────────────────────────────────────────────────┤
│ Option 层 (扩展语法) │
│ GFM 表格 / 任务列表 / 删除线 / 语法高亮 │
├─────────────────────────────────────────────────────┤
│ InlineParser │
│ 强调 / 代码 / 链接 / 图片 / HTML / 转义 / 自动链接 │
├─────────────────────────────────────────────────────┤
│ BlockParser │
│ 标题 / 段落 / 列表 / 代码块 / 引用 / 水平线 / 表格 │
├─────────────────────────────────────────────────────┤
│ Tokenizer / Lexer │
│ 行级分块 / 围栏检测 / 缩进分析 / 转义预处理 │
├─────────────────────────────────────────────────────┤
│ AST 类型定义 │
│ Block / Inline 枚举体系 │
└─────────────────────────────────────────────────────┘moonbit-texteditor/
├── moon.mod.json # 项目配置
├── src/
│ ├── moon.pkg.json # 包配置
│ ├── ast.mbt # AST 类型定义(核心数据结构)
│ ├── lexer.mbt # 词法分析:行分类、缩进计算、围栏检测
│ ├── block_parser.mbt # 块级解析器
│ ├── inline_parser.mbt # 内联解析器
│ ├── renderer_html.mbt # HTML 渲染器
│ ├── renderer.mbt # 渲染器 trait 定义(方便扩展其他输出)
│ ├── options.mbt # 解析选项配置
│ ├── syntax_highlight.mbt # 语法高亮引擎
│ ├── lib.mbt # 统一入口 API
│ └── utils.mbt # 工具函数
├── cmd/main/
│ ├── moon.pkg.json # CLI demo 包配置
│ └── main.mbt # CLI 演示程序(13 个功能场景)
├── web/
│ ├── moon.pkg.json # Web 包配置
│ └── main.mbt # 浏览器端 JS 桥接入口
├── demo.html # 交互式 Web 演示页面
└── .github/workflows/
├── ci.yml # CI(check / build / test)
└── copilot-setup-steps.yml # Copilot 环境配置// === Block 节点类型 ===
enum Block {
// 文档根节点
Document(Array[Block])
// 段落
Paragraph(Array[Inline])
// 标题 (level: 1-6)
Heading(Int, Array[Inline])
// 无序列表
BulletList(Array[ListItem], Bool) // 是否紧凑
// 有序列表
OrderedList(Array[ListItem], Int, Bool) // 起始序号, 是否紧凑
// 列表项
ListItem(Bool, Array[Block]) // 是否已勾选(任务列表)
// 代码块(围栏式)
FencedCodeBlock(String?, String) // 语言, 内容
// 代码块(缩进式)
IndentedCodeBlock(String)
// 块引用
BlockQuote(Array[Block])
// 水平线
ThematicBreak
// HTML 块
HtmlBlock(String)
// 表格(GFM)
Table(Array[TableRow])
// 空行
BlankLine
}
// === Inline 节点类型 ===
enum Inline {
// 纯文本
Text(String)
// 软换行
SoftBreak
// 硬换行
LineBreak
// 斜体
Emphasis(Array[Inline])
// 加粗
Strong(Array[Inline])
// 删除线(GFM)
Strikethrough(Array[Inline])
// 行内代码
Code(String)
// 链接
Link(Array[Inline], String, String?) // 文本, URL, title
// 图片
Image(String, String, String?) // alt, src, title
// HTML 内联
HtmlInline(String)
// 自动链接
AutoLink(String, Bool) // URL / Email
}enum LineType {
Heading(Int) // # ## ### ...
FenceStart(String?) // ``` 或 ~~~,可选语言
FenceEnd
BulletItem(String) // - * +
OrderedItem(Int) // 1.
BlockQuote
ThematicBreak
Empty
IndentedCode
TableRow
Paragraph
}
struct Line {
typ : LineType
content : String
indent : Int
line_number : Int
}struct BlockParser {
lines : Array[Line]
mut pos : Int
}
fn parse(lines : Array[Line]) -> Array[Block]struct Delimiter {
char : Char // * 或 _
is_close : Bool
is_open : Bool
position : Int
length : Int // 1 或 2
}fn render_block(block : Block, buf : StringBuilder) -> Unit
fn render_inline(inline : Inline, buf : StringBuilder) -> Unitstruct MarkdownOptions {
// 启用/禁用特定语法
enable_gfm_tables : Bool = false
enable_gfm_tasklist : Bool = false
enable_gfm_strikethrough : Bool = false
enable_gfm_autolink : Bool = false
enable_syntax_highlight : Bool = false
// 渲染选项
line_width : Int = 80
hard_wrap : Bool = false // 是否将软换行转为 <br>
}/// 将 Markdown 字符串解析为 Block AST
pub fn parse(input : String) -> Array[Block]
/// 将 Block AST 渲染为 HTML 字符串
pub fn render(blocks : Array[Block]) -> String
/// 解析 + 渲染一站式调用
pub fn render_to_string(input : String) -> String
/// 带选项的解析
pub fn parse_with_opts(input : String, opts : MarkdownOptions) -> Array[Block]
/// 带选项的渲染
pub fn render_to_string_with_opts(input : String, opts : MarkdownOptions) -> String| 模块 | 文件 | 预估代码量 |
|---|---|---|
| AST 类型定义 | ast.mbt | ~0.5k |
| 词法分析器 | lexer.mbt | ~1.0k |
| 块级解析器 | block_parser.mbt | ~2.0k |
| 内联解析器 | inline_parser.mbt | ~2.5k |
| HTML 渲染器 | renderer_html.mbt + renderer.mbt | ~1.5k |
| 选项系统 | options.mbt | ~0.3k |
| 统一 API | lib.mbt | ~0.2k |
| 工具函数 | utils.mbt | ~0.3k |
| 小计(核心) | ~8.3k | |
| GFM 表格 | gfm/table.mbt | ~0.8k |
| GFM 任务列表 | gfm/task_list.mbt | ~0.3k |
| GFM 删除线 | gfm/strikethrough.mbt | ~0.2k |
| GFM 自动链接 | gfm/autolink.mbt | ~0.3k |
| 小计(扩展) | ~1.6k | |
| 测试 | test/*.mbt | ~1.5k |
| 基准测试 | bench/benchmark.mbt | ~0.5k |
| 合计 | ~11.9k |
单元测试 → 集成测试 → CommonMark 规范测试输入: "# Hello"
输出: "<h1>Hello</h1>"
输入: "**bold** and *italic*"
输出: "<p><strong>bold</strong> and <em>italic</em></p>"
输入: "- item 1\n- item 2"
输出: "<ul>\n<li>item 1</li>\n<li>item 2</li>\n</ul>"
输入: "> quote\n>\n> more"
输出: "<blockquote>\n<p>quote</p>\n<p>more</p>\n</blockquote>"| 特性 | 用途 |
|---|---|
| enum + match | AST 节点枚举 + 模式匹配解析/渲染 |
| StringBuilder | 高效 HTML 拼接(MoonBit v0.10 优化) |
| <+ 模板写入语法 | HTML 渲染时避免重复 write_string 调用 |
| trait | 渲染器接口抽象(后续可扩展 JSON / LaTeX 输出) |
| raise 错误处理 | 解析错误传播 |
| 风险 | 应对方案 |
|---|---|
| 强调解析实现复杂(CommonMark 规则繁琐) | 参考实现规范中的伪代码,分步实现 delimiter 处理逻辑 |
| 嵌套列表边界情况多 | 使用递归下降 + 缩进栈管理,测试覆盖所有嵌套场景 |
| 代码量不足 4k | 提前规划 GFM 扩展和语法高亮作为"代码量缓冲" |
| MoonBit API 版本变化 | 锁定月亮版本,关注更新日志,统一适配 |
| 项目名称 | 链接 | 许可证 | 参考内容 |
|---|---|---|---|
| CommonMark Spec | https://spec.commonmark.org/0.31/ | CC-BY-SA 4.0 | 语法规则、解析算法伪代码、测试用例 |
Install
Download zipA pure MoonBit Markdown to HTML conversion library.