什么是 Markdown
Markdown 是一种轻量级标记语言,由 John Gruber 和 Aaron Swartz 在 2004 年创建,它允许人们使用易读易写的纯文本格式编写文档。
Markdown 语法简介
- 标题:使用
#来定义标题,#的数量代表标题的级别 - 强调:使用
*或_来包含文本可以实现斜体、粗体等效果。例如*斜体*和**粗体** - 列表:使用
*、+或-来创建无序列表,使用数字后面跟.来创建有序列表 - 链接和图片:使用
[描述](URL)来创建链接,使用![描述][URL]来插入图片 - 引用:使用
>来引用文本 - 代码:使用一对反引号 ` 来标记行内代码,使用三对反引号 ``` 来标记代码块
Markdown 文件通常以 .md 或 .markdown 作为文件后缀名
swift-markdown-ui
swift-markdown-ui 是一个开源项目,用于在 SwiftUI 中显示和自定义 Markdown 文本,demo 如下:
struct CodeView: View {
private let content = #"""
You can call out code or a command within a sentence with single backticks.
The text within the backticks will not be formatted.
Use `git status` to list all new or modified files that haven't yet been committed.
```swift
Group {
Text("SwiftUI")
Text("Combine")
Text("Swift System")
}
.font(.headline)
```
"""#
var body: some View {
DemoView {
Markdown(self.content)
}
}
}
数据 MarkdownContent
MarkdownContent 的数据结构是一个 BlockNode 类型的数组。
public struct MarkdownContent {
let blocks: [BlockNode]
}
BlockNode 是一个枚举类型,它具体类型包括 blockquote, bulletedList, numberedList, taskList, codeBlock, htmlBlock, paragraph, heading, table, thematicBreak。
enum BlockNode: Hashable {
case blockquote(children: [BlockNode])
case bulletedList(isTight: Bool, items: [RawListItem])
case numberedList(isTight: Bool, start: Int, items: [RawListItem])
case taskList(isTight: Bool, items: [RawTaskListItem])
case codeBlock(fenceInfo: String?, content: String)
case htmlBlock(content: String)
case paragraph(content: [InlineNode])
case heading(level: Int, content: [InlineNode])
case table(columnAlignments: [RawTableColumnAlignment], rows: [RawTableRow])
case thematicBreak
}
一个块节点 BlockNode 可能包含若干子块节点 children BlockNode,也可能包含若干行内节点 InlineNode。InlineNode 也是一个枚举类型,它的具体类型包括 text, softBreak, lineBreak, code, html, emphasis, strong, strikethrough, link, image。
enum InlineNode: Hashable {
case text(String)
case softBreak
case lineBreak
case code(String)
case html(String)
case emphasis(children: [InlineNode])
case strong(children: [InlineNode])
case strikethrough(children: [InlineNode])
case link(destination: String, children: [InlineNode])
case image(source: String, children: [InlineNode])
}
解析器 MarkdownParser
MarkdownParser 使用 cmark-gfm 解析 markdown 字符串。
cmark-gfm 是一个开源的由 C 语言编写的 Markdown 快速解析器,它是 cmark 的一个扩展,用于支持 GitHub Flavored Markdown(GFM)。
GFM 是 Markdown 的一个扩展版本,GitHub 在其平台上的 README 文件使用了这种格式。MarkdownParser 将 cmark-gfm 解析的结果,转换成 BlockNode 数组。
具体流程如下:string -> cmark-gfm -> MarkdownParser -> [BlockNode]
视图 Markdown
每种类型的节点都有对应的 View 去渲染。
extension BlockNode: View {
var body: some View {
switch self {
case .blockquote(let children):
BlockquoteView(children: children)
case .bulletedList(let isTight, let items):
BulletedListView(isTight: isTight, items: items)
case .numberedList(let isTight, let start, let items):
NumberedListView(isTight: isTight, start: start, items: items)
case .taskList(let isTight, let items):
TaskListView(isTight: isTight, items: items)
case .codeBlock(let fenceInfo, let content):
CodeBlockView(fenceInfo: fenceInfo, content: content)
case .htmlBlock(let content):
ParagraphView(content: content)
case .paragraph(let content):
ParagraphView(content: content)
case .heading(let level, let content):
HeadingView(level: level, content: content)
case .table(let columnAlignments, let rows):
if #available(iOS 16.0, macOS 13.0, tvOS 16.0, watchOS 9.0, *) {
TableView(columnAlignments: columnAlignments, rows: rows)
}
case .thematicBreak:
ThematicBreakView()
}
}
}
主题 Theme
支持三种不同的主题 Basic, DocC 和 GitHub,可通过调用 markdownTheme(_:) 切换主题。