yuin/goldmark
GitHub: yuin/goldmark
一个用 Go 编写的、完全兼容 CommonMark 标准且高度可扩展的 Markdown 解析器。
Stars: 4907 | Forks: 297
# goldmark
[](https://pkg.go.dev/github.com/yuin/goldmark)
[](https://github.com/yuin/goldmark/actions?query=workflow:test)
[](https://coveralls.io/github/yuin/goldmark)
[](https://goreportcard.com/report/github.com/yuin/goldmark)
goldmark 完全兼容 CommonMark 0.31.2。
- [goldmark 演练场](https://yuin.github.io/goldmark/playground/) :在线试用 goldmark。此演练场使用 WASM (5-10MB) 构建。
此外还有一个 Rust 版本的 goldmark:[rushdown](https://github.com/yuin/rushdown)
## 动机
我需要一个满足以下要求的 Go Markdown 解析器:
- 易于扩展。
- 与 reStructuredText 等其他轻量级标记语言相比,Markdown 的文档表达能力较弱。
- 我们有许多 Markdown 语法的扩展,例如 PHP Markdown Extra、GitHub Flavored Markdown。
- 标准兼容。
- Markdown 有很多方言。
- GitHub-Flavored Markdown 被广泛使用,并且基于 CommonMark,这实际上使得 CommonMark 是否是一个理想规范的问题变得没有实际意义。
- CommonMark 很复杂且难以实现。
- 结构良好。
- 基于 AST;保留节点的源位置。
- 使用纯 Go 编写。
[golang-commonmark](https://gitlab.com/golang-commonmark/markdown) 可能是一个不错的选择,但它似乎是 [markdown-it](https://github.com/markdown-it) 的复制品。
[blackfriday.v2](https://github.com/russross/blackfriday/tree/v2) 是一个快速且广泛使用的实现,但它不兼容 CommonMark,并且由于其 AST 使用的是 struct 而非 interface,因此无法从 package 外部进行扩展。
此外,在某些情况下它的行为与其他实现不同,尤其是在列表方面:[Deep nested lists don't output correctly #329](https://github.com/russross/blackfriday/issues/329)、[List block cannot have a second line #244](https://github.com/russross/blackfriday/issues/244) 等。
这种行为有时会导致问题。如果你将 Markdown 文本从 GitHub 迁移到基于 blackfriday 的 wiki 上,许多列表会立即损坏。
如上所述,CommonMark 既复杂又难以实现,因此基于 CommonMark 的 Markdown 解析器非常少见。
## 特性
- **标准兼容。** goldmark 完全兼容最新的 [CommonMark](https://commonmark.org/) 规范。
- **可扩展。** 你想为 Markdown 添加 `@username` 提及语法吗?
在 goldmark 中你可以轻松做到这一点。你可以添加你的 AST 节点、
块级元素的 parser、行内元素的 parser、
段落的 transformer、整个 AST 结构的 transformer 以及
renderer。
- **性能。** goldmark 的性能与 cmark(用 C 编写的 CommonMark 参考实现)不相上下。
- **健壮。** goldmark 使用 `go test --fuzz` 进行了测试。
- **内置扩展。** goldmark 附带了常见的扩展,如表格、删除线、
任务列表和定义列表。
- **仅依赖标准库。**
## 安装
```
$ go get github.com/yuin/goldmark
```
## 用法
引入 package:
```
import (
"bytes"
"github.com/yuin/goldmark"
)
```
以兼容 CommonMark 的模式转换 Markdown 文档:
```
var buf bytes.Buffer
if err := goldmark.Convert(source, &buf); err != nil {
panic(err)
}
```
## 配合选项使用
```
var buf bytes.Buffer
if err := goldmark.Convert(source, &buf, parser.WithContext(ctx)); err != nil {
panic(err)
}
```
| 功能选项 | 类型 | 描述 |
| ----------------- | ---- | ----------- |
| `parser.WithContext` | A `parser.Context` | 解析阶段的上下文。 |
## 上下文选项
| 功能选项 | 类型 | 描述 |
| ----------------- | ---- | ----------- |
| `parser.WithIDs` | A `parser.IDs` | `IDs` 允许你更改与元素 id 相关的逻辑(例如:自动生成标题 id)。 |
## 自定义 parser 和 renderer
```
import (
"bytes"
"github.com/yuin/goldmark"
"github.com/yuin/goldmark/extension"
"github.com/yuin/goldmark/parser"
"github.com/yuin/goldmark/renderer/html"
)
md := goldmark.New(
goldmark.WithExtensions(extension.GFM),
goldmark.WithParserOptions(
parser.WithAutoHeadingID(),
),
goldmark.WithRendererOptions(
html.WithHardWraps(),
html.WithXHTML(),
),
)
var buf bytes.Buffer
if err := md.Convert(source, &buf); err != nil {
panic(err)
}
```
| 功能选项 | 类型 | 描述 |
| ----------------- | ---- | ----------- |
| `goldmark.WithParser` | `parser.Parser` | 此选项必须在 `goldmark.WithParserOptions` 和 `goldmark.WithExtensions` 之前传入 |
| `goldmark.WithRenderer` | `renderer.Renderer` | 此选项必须在 `goldmark.WithRendererOptions` 和 `goldmark.WithExtensions` 之前传入 |
| `goldmark.WithParserOptions` | `...parser.Option` | |
| `goldmark.WithRendererOptions` | `...renderer.Option` | |
| `goldmark.WithExtensions` | `...goldmark.Extender` | |
## Parser 和 Renderer 选项
### Parser 选项
| 功能选项 | 类型 | 描述 |
| ----------------- | ---- | ----------- |
| `parser.WithBlockParsers` | 元素为 `parser.BlockParser` 的 `util.PrioritizedSlice` | 用于解析块级元素的 parser。 |
| `parser.WithInlineParsers` | 元素为 `parser.InlineParser` 的 `util.PrioritizedSlice` | 用于解析行内级元素的 parser。 |
| `parser.WithParagraphTransformers` | 元素为 `parser.ParagraphTransformer` 的 `util.PrioritizedSlice` | 用于转换段落节点的 transformer。 |
| `parser.WithASTTransformers` | 元素为 `parser.ASTTransformer` 的 `util.PrioritizedSlice` | 用于转换 AST 的 transformer。 |
| `parser.WithAutoHeadingID` | `-` | 启用自动标题 id。 |
| `parser.WithAttribute` | `-` | 启用自定义属性。目前仅标题支持属性。 |
### HTML Renderer 选项
| 功能选项 | 类型 | 描述 |
| ----------------- | ---- | ----------- |
| `html.WithWriter` | `html.Writer` | 用于将内容写入 `io.Writer` 的 `html.Writer`。 |
| `html.WithHardWraps` | `-` | 将换行符渲染为 `
`。| | `html.WithXHTML` | `-` | 渲染为 XHTML。 | | `html.WithUnsafe` | `-` | 默认情况下,goldmark 不会渲染原始 HTML 或潜在危险的链接。使用此选项,goldmark 会按照原样渲染此类内容。 | ### 内置扩展 - `extension.Table` - [GitHub Flavored Markdown: Tables](https://github.github.com/gfm/#tables-extension-) - `extension.Strikethrough` - [GitHub Flavored Markdown: Strikethrough](https://github.github.com/gfm/#strikethrough-extension-) - `extension.Linkify` - [GitHub Flavored Markdown: Autolinks](https://github.github.com/gfm/#autolinks-extension-) - `extension.TaskList` - [GitHub Flavored Markdown: Task list items](https://github.github.com/gfm/#task-list-items-extension-) - `extension.GFM` - 此扩展启用了 Table、Strikethrough、Linkify 和 TaskList。 - 此扩展不会过滤在 [6.11: Disallowed Raw HTML (extension)](https://github.github.com/gfm/#disallowed-raw-html-extension-) 中定义的标签。 如果你需要过滤 HTML 标签,请参阅 [安全性](#security)。 - 如果你需要解析 GitHub 的 emoji,你可以使用 [goldmark-emoji](https://github.com/yuin/goldmark-emoji) 扩展。 - `extension.DefinitionList` - [PHP Markdown Extra: Definition lists](https://michelf.ca/projects/php-markdown/extra/#def-list) - `extension.Footnote` - [PHP Markdown Extra: Footnotes](https://michelf.ca/projects/php-markdown/extra/#footnotes) - `extension.Typographer` - 此扩展将标点符号替换为排版实体,类似于 [smartypants](https://daringfireball.net/projects/smartypants/)。 - `extension.CJK` - 此扩展是与 CJK 相关功能的快捷方式。 ### 属性 `parser.WithAttribute` 选项允许你在某些元素上定义属性。 **属性正在 [CommonMark 论坛](https://talk.commonmark.org/t/consistent-attribute-syntax/272) 中进行讨论。此语法在未来可能会发生改变。** #### 标题 ``` ## 标题 ## {#id .className attrName=attrValue class="class1 class2"} ## 标题 {#id .className attrName=attrValue class="class1 class2"} ``` ``` heading {#id .className attrName=attrValue} ============ ``` ### Table 扩展 Table 扩展实现了 [Table(extension)](https://github.github.com/gfm/#tables-extension-),正如 [GitHub Flavored Markdown Spec](https://github.github.com/gfm/) 中所定义。 规范是针对 XHTML 定义的,因此规范使用了一些在 HTML5 中已废弃的属性。 你可以通过选项覆盖对齐方式的渲染方法。 | 功能选项 | 类型 | 描述 | | ----------------- | ---- | ----------- | | `extension.WithTableCellAlignMethod` | `extension.TableCellAlignMethod` | 指示表格单元格如何对齐的选项。 | ### Typographer 扩展 Typographer 扩展将纯 ASCII 标点字符转换为排版标点 HTML 实体。 默认替换项如下: | 标点符号 | 默认实体 | | ------------ | ---------- | | `'` | `‘`, `’` | | `"` | `“`, `”` | | `--` | `–` | | `---` | `—` | | `...` | `…` | | `<<` | `«` | | `>>` | `»` | 你可以通过 `extensions.WithTypographicSubstitutions` 覆盖默认的替换项: ``` markdown := goldmark.New( goldmark.WithExtensions( extension.NewTypographer( extension.WithTypographicSubstitutions(extension.TypographicSubstitutions{ extension.LeftSingleQuote: []byte("‚"), extension.RightSingleQuote: nil, // nil disables a substitution }), ), ), ) ``` ### Linkify 扩展 Linkify 扩展实现了 [Autolinks(extension)](https://github.github.com/gfm/#autolinks-extension-),正如 [GitHub Flavored Markdown Spec](https://github.github.com/gfm/) 中所定义。 由于规范并未明确定义关于 URL 的细节,因此存在许多模棱两可的情况。 你可以通过选项覆盖自动链接的模式。 | 功能选项 | 类型 | 描述 | | ----------------- | ---- | ----------- | | `extension.WithLinkifyAllowedProtocols` | `[][]byte \| []string` | 允许的协议列表,例如 `[]string{ "http:" }` | | `extension.WithLinkifyURLRegexp` | `*regexp.Regexp` | 定义 URL(包括协议)的正则表达式 | | `extension.WithLinkifyWWWRegexp` | `*regexp.Regexp` | 定义以 `www.` 开头的 URL 的正则表达式。此模式对应于 [the extended www autolink](https://github.github.com/gfm/#extended-www-autolink) | | `extension.WithLinkifyEmailRegexp` | `*regexp.Regexp` | 定义电子邮件地址的正则表达式` | 示例,使用 [xurls](https://github.com/mvdan/xurls): ``` import "mvdan.cc/xurls/v2" markdown := goldmark.New( goldmark.WithRendererOptions( html.WithXHTML(), html.WithUnsafe(), ), goldmark.WithExtensions( extension.NewLinkify( extension.WithLinkifyAllowedProtocols([]string{ "http:", "https:", }), extension.WithLinkifyURLRegexp( xurls.Strict(), ), ), ), ) ``` ### Footnotes 扩展 Footnotes 扩展实现了 [PHP Markdown Extra: Footnotes](https://michelf.ca/projects/php-markdown/extra/#footnotes)。 此扩展有一些选项: | 功能选项 | 类型 | 描述 | | ----------------- | ---- | ----------- | | `extension.WithFootnoteIDPrefix` | `[]byte \| string` | id 属性的前缀。| | `extension.WithFootnoteIDPrefixFunction` | `func(gast.Node) []byte` | 为给定 Node 确定 id 属性的函数。| | `extension.WithFootnoteLinkTitle` | `[]byte \| string` | 脚注链接的可选 title 属性。| | `extension.WithFootnoteBacklinkTitle` | `[]byte \| string` | 脚注反向链接的可选 title 属性。 | | `extension.WithFootnoteLinkClass` | `[]byte \| string` | 脚注链接的 class。默认为 `footnote-ref`。 | | `extension.WithFootnoteBacklinkClass` | `[]byte \| string` | 脚注反向链接的 class。默认为 `footnote-backref`。 | | `extension.WithFootnoteBacklinkHTML` | `[]byte \| string` | 脚注反向链接的 class。默认为 `↩︎`。 | 某些选项可以有特殊的替换。字符串中出现的 “^^” 将被替换为 HTML 输出中相应的脚注编号。出现的 “%%” 将被替换为引用的编号(脚注可以有多个引用)。 如果在同一个 HTML 文档中显示多个 Markdown 文档,`extension.WithFootnoteIDPrefix` 和 `extension.WithFootnoteIDPrefixFunction` 对于避免脚注 id 相互冲突非常有用。 `extension.WithFootnoteIDPrefix` 设置固定的 id 前缀,因此你可以编写如下代码: ``` for _, path := range files { source := readAll(path) prefix := getPrefix(path) markdown := goldmark.New( goldmark.WithExtensions( NewFootnote( WithFootnoteIDPrefix(path), ), ), ) var b bytes.Buffer err := markdown.Convert(source, &b) if err != nil { t.Error(err.Error()) } } ``` `extension.WithFootnoteIDPrefixFunction` 通过调用给定的函数来确定 id 前缀,因此你可以编写如下代码: ``` markdown := goldmark.New( goldmark.WithExtensions( NewFootnote( WithFootnoteIDPrefixFunction(func(n gast.Node) []byte { v, ok := n.OwnerDocument().Meta()["footnote-prefix"] if ok { return util.StringToReadOnlyBytes(v.(string)) } return nil }), ), ), ) for _, path := range files { source := readAll(path) var b bytes.Buffer doc := markdown.Parser().Parse(text.NewReader(source)) doc.Meta()["footnote-prefix"] = getPrefix(path) err := markdown.Renderer().Render(&b, source, doc) } ``` 你可以使用 [goldmark-meta](https://github.com/yuin/goldmark-meta) 在 Markdown 文档中定义 id 前缀: ``` --- title: document title slug: article1 footnote-prefix: article1 --- # 我的文章 ``` ### CJK 扩展 CommonMark 高度重视兼容性,且原始 Markdown 是由西方人设计的。因此 CommonMark 缺乏对 CJK 等语言的考量。 此扩展为 CJK 用户提供了额外的选项。 | 功能选项 | 类型 | 描述 | | ----------------- | ---- | ----------- | | `extension.WithEastAsianLineBreaks` | `...extension.EastAsianLineBreaksStyle` | 软换行被渲染为换行符。一些亚洲用户会将其视为不必要的空格。启用此选项后,东亚宽字符之间的软换行将被忽略。默认为 `EastAsianLineBreaksStyleSimple`。 | | `extension.WithEscapedSpace` | `-` | 如果在以东亚标点符号开头的强调标记周围没有空格,它将不会被解析为强调(根据 CommonMark 规范定义)。启用选项后,你可以通过在强调标记周围添加“不渲染的”空格来避免这种不便的行为,例如 `太郎は\ **「こんにちわ」**\ といった`。 | #### 换行风格 | 风格 | 描述 | | ----- | ----------- | | `EastAsianLineBreaksStyleSimple` | 如果换行两侧都是东亚宽字符,则忽略软换行。此行为与 Pandoc 中的 [`east_asian_line_breaks`](https://pandoc.org/MANUAL.html#extension-east_asian_line_breaks) 相同。 | | `EastAsianLineBreaksCSS3Draft` | 此选项实现了 CSS 文本 level3 的 [Segment Break Transformation Rules](https://drafts.csswg.org/css-text-3/#line-break-transform) 并进行了一些 [增强](https://github.com/w3c/csswg-drafts/issues/5086)。 | #### `EastAsianLineBreaksStyleSimple` 示例 输入 Markdown: ``` 私はプログラマーです。 東京の会社に勤めています。 GoでWebアプリケーションを開発しています。 ``` 输出: ```` 元素的支持。
- [goldmark-frontmatter](https://github.com/abhinav/goldmark-frontmatter):为文档添加对 YAML、TOML 和自定义 front matter 的支持。
- [goldmark-toc](https://github.com/abhinav/goldmark-toc):为 goldmark 文档添加生成目录的支持。
- [goldmark-mermaid](https://github.com/abhinav/goldmark-mermaid):添加在 goldmark 文档中渲染 [Mermaid](https://mermaid-js.github.io/mermaid/) 图表的支持。
- [goldmark-pikchr](https://github.com/jchenry/goldmark-pikchr):添加在 goldmark 文档中渲染 [Pikchr](https://pikchr.org/home/doc/trunk/homepage.md) 图表的支持。
- [goldmark-embed](https://github.com/13rac1/goldmark-embed):添加从 YouTube 链接渲染嵌入内容的支持。
- [goldmark-latex](https://github.com/soypat/goldmark-latex):一个可以传递给 `goldmark.WithRenderer()` 的 $\LaTeX$ renderer。
- [goldmark-fences](https://github.com/stefanfritsch/goldmark-fences):在 goldmark 中支持 pandoc 风格的 [fenced divs](https://pandoc.org/MANUAL.html#divs-and-spans)。
- [goldmark-d2](https://github.com/FurqanSoftware/goldmark-d2):添加对 [D2](https://d2lang.com/) 图表的支持。
- [goldmark-katex](https://github.com/FurqanSoftware/goldmark-katex):添加对 [KaTeX](https://katex.org/) 数学公式和方程的支持。
- [goldmark-img64](https://github.com/tenkoh/goldmark-img64):添加将图像作为 DataURL(base64 编码)嵌入到文档中的支持。
- [goldmark-enclave](https://github.com/quailyquaily/goldmark-enclave):添加对嵌入 youtube/bilibili 视频、X 的 [oembed X](https://publish.x.com/)、[tradingview chart](https://www.tradingview.com/widget/) 的图表、[quaily widget](https://quaily.com)、[spotify embeds](https://developer.spotify.com/documentation/embeds)、[dify embed](https://dify.ai/) 以及 html 音频到文档中的支持。
- [goldmark-wiki-table](https://github.com/movsb/goldmark-wiki-table):添加对嵌入 Wiki 表格的支持。
- [goldmark-tgmd](https://github.com/Mad-Pixels/goldmark-tgmd):一个可以传递给 `goldmark.WithRenderer()` 的 Telegram markdown renderer。
- [goldmark-treeblood](https://github.com/Wyatt915/goldmark-treeblood):将 $\LaTeX$ 表达式渲染为 MathML(纯 Go 实现,无外部依赖)。
- [goldmark-subtext](https://github.com/zeozeozeo/goldmark-subtext):支持 Discord 风格的 markdown 副标题。
- [goldmark-customtag](https://github.com/tendstofortytwo/goldmark-customtag):允许你定义自定义块级标签。
- [goldmark-cjk-friendly](https://github.com/tats-u/goldmark-cjk-friendly):将 npm package [`remark-cjk-friendly` / `markdown-it-cjk-friendly`](https://github.com/tats-u/markdown-cjk-friendly) 移植到 goldmark。类似于 [CJK 扩展](#cjk-extension) (`WithEscapedSpace`),但你不需要显式地在 `*` 和 `**` 周围添加 `\ `。你可以将其与 [CJK 扩展](#cjk-extension) 结合使用。
- [goldmark-chart](https://github.com/TheGreatRambler/goldmark-chart):使用简单的 [Markvis](https://markvis.js.org/#/) 格式生成静态 ChartJS 图表。
### 在运行时加载扩展
[goldmark-dynamic](https://github.com/yuin/goldmark-dynamic) 允许你使用 Lua 编写 goldmark 扩展,并在运行时加载它而无需重新编译。
详情请参阅 [goldmark-dynamic](https://github.com/yuin/goldmark-dynamic)。
## goldmark 内部机制(面向扩展开发者)
### 概述
goldmark 的 Markdown 处理流程如以下图表所示。
```
|
V
+-------- parser.Parser ---------------------------
| 1. Parse block elements into AST
| 1. If a parsed block is a paragraph, apply
| ast.ParagraphTransformer
| 2. Traverse AST and parse blocks.
| 1. Process delimiters(emphasis) at the end of
| block parsing
| 3. Apply parser.ASTTransformers to AST
|
V
|
V
+------- renderer.Renderer ------------------------
| 1. Traverse AST and apply renderer.NodeRenderer
| corespond to the node type
|
V
`。| | `html.WithXHTML` | `-` | 渲染为 XHTML。 | | `html.WithUnsafe` | `-` | 默认情况下,goldmark 不会渲染原始 HTML 或潜在危险的链接。使用此选项,goldmark 会按照原样渲染此类内容。 | ### 内置扩展 - `extension.Table` - [GitHub Flavored Markdown: Tables](https://github.github.com/gfm/#tables-extension-) - `extension.Strikethrough` - [GitHub Flavored Markdown: Strikethrough](https://github.github.com/gfm/#strikethrough-extension-) - `extension.Linkify` - [GitHub Flavored Markdown: Autolinks](https://github.github.com/gfm/#autolinks-extension-) - `extension.TaskList` - [GitHub Flavored Markdown: Task list items](https://github.github.com/gfm/#task-list-items-extension-) - `extension.GFM` - 此扩展启用了 Table、Strikethrough、Linkify 和 TaskList。 - 此扩展不会过滤在 [6.11: Disallowed Raw HTML (extension)](https://github.github.com/gfm/#disallowed-raw-html-extension-) 中定义的标签。 如果你需要过滤 HTML 标签,请参阅 [安全性](#security)。 - 如果你需要解析 GitHub 的 emoji,你可以使用 [goldmark-emoji](https://github.com/yuin/goldmark-emoji) 扩展。 - `extension.DefinitionList` - [PHP Markdown Extra: Definition lists](https://michelf.ca/projects/php-markdown/extra/#def-list) - `extension.Footnote` - [PHP Markdown Extra: Footnotes](https://michelf.ca/projects/php-markdown/extra/#footnotes) - `extension.Typographer` - 此扩展将标点符号替换为排版实体,类似于 [smartypants](https://daringfireball.net/projects/smartypants/)。 - `extension.CJK` - 此扩展是与 CJK 相关功能的快捷方式。 ### 属性 `parser.WithAttribute` 选项允许你在某些元素上定义属性。 **属性正在 [CommonMark 论坛](https://talk.commonmark.org/t/consistent-attribute-syntax/272) 中进行讨论。此语法在未来可能会发生改变。** #### 标题 ``` ## 标题 ## {#id .className attrName=attrValue class="class1 class2"} ## 标题 {#id .className attrName=attrValue class="class1 class2"} ``` ``` heading {#id .className attrName=attrValue} ============ ``` ### Table 扩展 Table 扩展实现了 [Table(extension)](https://github.github.com/gfm/#tables-extension-),正如 [GitHub Flavored Markdown Spec](https://github.github.com/gfm/) 中所定义。 规范是针对 XHTML 定义的,因此规范使用了一些在 HTML5 中已废弃的属性。 你可以通过选项覆盖对齐方式的渲染方法。 | 功能选项 | 类型 | 描述 | | ----------------- | ---- | ----------- | | `extension.WithTableCellAlignMethod` | `extension.TableCellAlignMethod` | 指示表格单元格如何对齐的选项。 | ### Typographer 扩展 Typographer 扩展将纯 ASCII 标点字符转换为排版标点 HTML 实体。 默认替换项如下: | 标点符号 | 默认实体 | | ------------ | ---------- | | `'` | `‘`, `’` | | `"` | `“`, `”` | | `--` | `–` | | `---` | `—` | | `...` | `…` | | `<<` | `«` | | `>>` | `»` | 你可以通过 `extensions.WithTypographicSubstitutions` 覆盖默认的替换项: ``` markdown := goldmark.New( goldmark.WithExtensions( extension.NewTypographer( extension.WithTypographicSubstitutions(extension.TypographicSubstitutions{ extension.LeftSingleQuote: []byte("‚"), extension.RightSingleQuote: nil, // nil disables a substitution }), ), ), ) ``` ### Linkify 扩展 Linkify 扩展实现了 [Autolinks(extension)](https://github.github.com/gfm/#autolinks-extension-),正如 [GitHub Flavored Markdown Spec](https://github.github.com/gfm/) 中所定义。 由于规范并未明确定义关于 URL 的细节,因此存在许多模棱两可的情况。 你可以通过选项覆盖自动链接的模式。 | 功能选项 | 类型 | 描述 | | ----------------- | ---- | ----------- | | `extension.WithLinkifyAllowedProtocols` | `[][]byte \| []string` | 允许的协议列表,例如 `[]string{ "http:" }` | | `extension.WithLinkifyURLRegexp` | `*regexp.Regexp` | 定义 URL(包括协议)的正则表达式 | | `extension.WithLinkifyWWWRegexp` | `*regexp.Regexp` | 定义以 `www.` 开头的 URL 的正则表达式。此模式对应于 [the extended www autolink](https://github.github.com/gfm/#extended-www-autolink) | | `extension.WithLinkifyEmailRegexp` | `*regexp.Regexp` | 定义电子邮件地址的正则表达式` | 示例,使用 [xurls](https://github.com/mvdan/xurls): ``` import "mvdan.cc/xurls/v2" markdown := goldmark.New( goldmark.WithRendererOptions( html.WithXHTML(), html.WithUnsafe(), ), goldmark.WithExtensions( extension.NewLinkify( extension.WithLinkifyAllowedProtocols([]string{ "http:", "https:", }), extension.WithLinkifyURLRegexp( xurls.Strict(), ), ), ), ) ``` ### Footnotes 扩展 Footnotes 扩展实现了 [PHP Markdown Extra: Footnotes](https://michelf.ca/projects/php-markdown/extra/#footnotes)。 此扩展有一些选项: | 功能选项 | 类型 | 描述 | | ----------------- | ---- | ----------- | | `extension.WithFootnoteIDPrefix` | `[]byte \| string` | id 属性的前缀。| | `extension.WithFootnoteIDPrefixFunction` | `func(gast.Node) []byte` | 为给定 Node 确定 id 属性的函数。| | `extension.WithFootnoteLinkTitle` | `[]byte \| string` | 脚注链接的可选 title 属性。| | `extension.WithFootnoteBacklinkTitle` | `[]byte \| string` | 脚注反向链接的可选 title 属性。 | | `extension.WithFootnoteLinkClass` | `[]byte \| string` | 脚注链接的 class。默认为 `footnote-ref`。 | | `extension.WithFootnoteBacklinkClass` | `[]byte \| string` | 脚注反向链接的 class。默认为 `footnote-backref`。 | | `extension.WithFootnoteBacklinkHTML` | `[]byte \| string` | 脚注反向链接的 class。默认为 `↩︎`。 | 某些选项可以有特殊的替换。字符串中出现的 “^^” 将被替换为 HTML 输出中相应的脚注编号。出现的 “%%” 将被替换为引用的编号(脚注可以有多个引用)。 如果在同一个 HTML 文档中显示多个 Markdown 文档,`extension.WithFootnoteIDPrefix` 和 `extension.WithFootnoteIDPrefixFunction` 对于避免脚注 id 相互冲突非常有用。 `extension.WithFootnoteIDPrefix` 设置固定的 id 前缀,因此你可以编写如下代码: ``` for _, path := range files { source := readAll(path) prefix := getPrefix(path) markdown := goldmark.New( goldmark.WithExtensions( NewFootnote( WithFootnoteIDPrefix(path), ), ), ) var b bytes.Buffer err := markdown.Convert(source, &b) if err != nil { t.Error(err.Error()) } } ``` `extension.WithFootnoteIDPrefixFunction` 通过调用给定的函数来确定 id 前缀,因此你可以编写如下代码: ``` markdown := goldmark.New( goldmark.WithExtensions( NewFootnote( WithFootnoteIDPrefixFunction(func(n gast.Node) []byte { v, ok := n.OwnerDocument().Meta()["footnote-prefix"] if ok { return util.StringToReadOnlyBytes(v.(string)) } return nil }), ), ), ) for _, path := range files { source := readAll(path) var b bytes.Buffer doc := markdown.Parser().Parse(text.NewReader(source)) doc.Meta()["footnote-prefix"] = getPrefix(path) err := markdown.Renderer().Render(&b, source, doc) } ``` 你可以使用 [goldmark-meta](https://github.com/yuin/goldmark-meta) 在 Markdown 文档中定义 id 前缀: ``` --- title: document title slug: article1 footnote-prefix: article1 --- # 我的文章 ``` ### CJK 扩展 CommonMark 高度重视兼容性,且原始 Markdown 是由西方人设计的。因此 CommonMark 缺乏对 CJK 等语言的考量。 此扩展为 CJK 用户提供了额外的选项。 | 功能选项 | 类型 | 描述 | | ----------------- | ---- | ----------- | | `extension.WithEastAsianLineBreaks` | `...extension.EastAsianLineBreaksStyle` | 软换行被渲染为换行符。一些亚洲用户会将其视为不必要的空格。启用此选项后,东亚宽字符之间的软换行将被忽略。默认为 `EastAsianLineBreaksStyleSimple`。 | | `extension.WithEscapedSpace` | `-` | 如果在以东亚标点符号开头的强调标记周围没有空格,它将不会被解析为强调(根据 CommonMark 规范定义)。启用选项后,你可以通过在强调标记周围添加“不渲染的”空格来避免这种不便的行为,例如 `太郎は\ **「こんにちわ」**\ といった`。 | #### 换行风格 | 风格 | 描述 | | ----- | ----------- | | `EastAsianLineBreaksStyleSimple` | 如果换行两侧都是东亚宽字符,则忽略软换行。此行为与 Pandoc 中的 [`east_asian_line_breaks`](https://pandoc.org/MANUAL.html#extension-east_asian_line_breaks) 相同。 | | `EastAsianLineBreaksCSS3Draft` | 此选项实现了 CSS 文本 level3 的 [Segment Break Transformation Rules](https://drafts.csswg.org/css-text-3/#line-break-transform) 并进行了一些 [增强](https://github.com/w3c/csswg-drafts/issues/5086)。 | #### `EastAsianLineBreaksStyleSimple` 示例 输入 Markdown: ``` 私はプログラマーです。 東京の会社に勤めています。 GoでWebアプリケーションを開発しています。 ``` 输出: ```
私はプログラマーです。東京の会社に勤めています。\nGoでWebアプリケーションを開発しています。
``` #### `EastAsianLineBreaksCSS3Draft` 示例 输入 Markdown: ``` 私はプログラマーです。 東京の会社に勤めています。 GoでWebアプリケーションを開発しています。 ``` 输出: ```私はプログラマーです。東京の会社に勤めています。GoでWebアプリケーションを開発しています。
``` ## 安全性 默认情况下,goldmark 不会渲染原始 HTML 或潜在危险的 URL。 如果你需要更好地控制不受信任的内容,建议你 使用诸如 [bluemonday](https://github.com/microcosm-cc/bluemonday) 之类的 HTML sanitizer。 ## 基准测试 你可以在 `_benchmark` 目录中运行此基准测试。 ### 对比其他 golang 库 blackfriday v2 似乎是最快的,但由于它不兼容 CommonMark,其性能不能与兼容 CommonMark 的库直接进行比较。 与此同时,goldmark 构建了干净且可扩展的 AST 结构, 实现了与 CommonMark 的完全兼容,并且消耗更少的内存,同时在速度上也相当快。 - MBP 2019 13″(i5, 16GB), Go1.17 ``` BenchmarkMarkdown/Blackfriday-v2-8 302 3743747 ns/op 3290445 B/op 20050 allocs/op BenchmarkMarkdown/GoldMark-8 280 4200974 ns/op 2559738 B/op 13435 allocs/op BenchmarkMarkdown/CommonMark-8 226 5283686 ns/op 2702490 B/op 20792 allocs/op BenchmarkMarkdown/Lute-8 12 92652857 ns/op 10602649 B/op 40555 allocs/op BenchmarkMarkdown/GoMarkdown-8 13 81380167 ns/op 2245002 B/op 22889 allocs/op ``` ### 对比 cmark(用 C 编写的 CommonMark 参考实现) - MBP 2019 13″(i5, 16GB), Go1.17 ``` ----------- cmark ----------- file: _data.md iteration: 50 average: 0.0044073057 sec ------- goldmark ------- file: _data.md iteration: 50 average: 0.0041611990 sec ``` 如你所见,goldmark 的性能与 cmark 不相上下。 ## 扩展 ### 扩展列表 - [goldmark-meta](https://github.com/yuin/goldmark-meta):goldmark Markdown 解析器的 YAML metadata 扩展。 - [goldmark-highlighting](https://github.com/yuin/goldmark-highlighting):goldmark Markdown 解析器的语法高亮扩展。 - [goldmark-emoji](https://github.com/yuin/goldmark-emoji):goldmark Markdown 解析器的 emoji 扩展。 - [goldmark-mathjax](https://github.com/litao91/goldmark-mathjax):为 goldmark markdown 解析器提供 Mathjax 支持。 - [goldmark-pdf](https://github.com/stephenafamo/goldmark-pdf):一个可以传递给 `goldmark.WithRenderer()` 的 PDF renderer。 - [goldmark-hashtag](https://github.com/abhinav/goldmark-hashtag):为 goldmark 添加对基于 `#hashtag` 的标签支持。 - [goldmark-wikilink](https://github.com/abhinav/goldmark-wikilink):为 goldmark 添加对 `[[wiki]]` 风格链接的支持。 - [goldmark-anchor](https://github.com/abhinav/goldmark-anchor):在文档中所有标题旁边添加锚点 (永久链接)。 - [goldmark-figure](https://github.com/mangoumbrella/goldmark-figure):添加对将包含图像的段落渲染为 `标签:EVTX分析, 日志审计, 自动化payload嵌入