Maaku
Maaku 框架提供了对 cmark-gfm 的 Swift 包装,并添加了 Swift 友好的 AST 表示。支持表格、删除线、自动链接和标签过滤器等 gfm 扩展。
Maaku 还支持插件约定,自定义渲染器可以使用。提供了一个插件示例。
TexturedMaaku 基于 Maaku 和 Texture,为 Swift 提供了本地 iOS CommonMark 渲染框架。
安装
CocoaPods
CocoaPods 是 Cocoa 项目的依赖管理器。您可以使用以下命令安装
$ gem install cocoapods
要使用 CocoaPods 将 Maaku 集成到您的 Xcode 项目中,请在 Podfile
中指定它
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '10.0'
use_frameworks!
target '<Your Target Name>' do
pod 'Maaku'
end
然后,运行以下命令
$ pod install
Carthage
Carthage 是一个去中心化的依赖管理器,它构建您的依赖关系,并为您提供二进制框架。
您可以使用以下命令通过 Homebrew 安装 Carthage
$ brew update
$ brew install carthage
要使用 Carthage 将 Maaku 集成到您的 Xcode 项目中,请在 Cartfile
中指定它
github "KristopherGBaker/Maaku" ~> 0.6.0
运行 carthage update
命令来构建框架,并将构建好的 Maaku.framework
拖入您的 Xcode 项目中。
Swift 包管理器
Swift 包管理器(https://swiftlang.cn/package-manager/)是一个用于自动分发 Swift 代码的工具,并集成到 swift
编译器中。它处于早期开发阶段,但 Maaku 支持在支持平台上使用。
您设置好 Swift 包之后,将 Maaku 作为依赖项添加到您的 Package.swift
中的 dependencies
值就像添加任何其他依赖项一样简单。
dependencies: [
.package(url: "https://github.com/KristopherGBaker/Maaku.git", from: "0.6.0")
]
使用 Swift PM 构建
为 macOS 构建
$ swift build -Xswiftc "-target" -Xswiftc "x86_64-apple-macosx10.14"
运行测试
$ swift test -Xswiftc "-target" -Xswiftc "x86_64-apple-macosx10.14"
核心
Document
是使用 Maaku 的主要接口。Document 可以通过传递表示 CommonMark 的 Data 或 String 来初始化。
let document = try Document(text: commonMark)
初始化 Document 将解析 CommonMark 并创建一个可访问的 AST。文档包含一个顶层块元素列表,每个元素可能包含其他块元素或内联元素。块元素可能是容器块或叶块。容器块可以包含其他块,而叶块则不能。
样式
采用节点协议的核心数据类型支持通过attributedText
方法转换为NSAttributedString(大多数类型都得到支持,但当前存在一些限制,尤其是在嵌入式图像和HTML方面——包括内嵌和块形式的HTML)。
可通过Style
类型指定由attributedText
方法使用的字体和颜色。提供了带有DefaultStyle
的默认实现。
CMark
CMark*类型(部分灵感来自CocoaMarkdown)在cmark-gfm之上为Swift提供了一组友好的接口。CMark类型可以独立于Core使用,只需在Podfile中包含Maaku/CMark
子规约即可。
插件
插件遵循一种约定,即插件以CommonMark文本中段落中单个链接的形式出现。以下是一个CommonMark中YouTube插件可能出现的形式示例
Some other markdown text.
[youtubevideo](https://youtu.be/kkdBB1hVLX0)
More markdown.
要支持此插件,我们需要实现两个协议:Plugin和PluginParser(在Plugin.swift
中定义,下文有示例展示)。
public protocol Plugin: LeafBlock {
static var pluginName: PluginName { get }
}
public protocol PluginParser {
var name: String { get }
func parse(text: String) -> Plugin?
}
插件示例
YouTube插件作为示例提供在框架中(如果你使用CocoaPods安装,它是一个可选的子规约)。
public struct YoutubePlugin: Plugin {
public static let pluginName: PluginName = "youtubevideo"
public let url: URL
public var videoId: String? {
return url.path.components(separatedBy: "/").last
}
public init(url: URL) {
self.url = url
}
}
应该唯一地为插件指定pluginName
值。它可以与用于PluginParser的name
相同,但不需要相同。
PluginParser示例
public struct YoutubePluginParser: PluginParser {
public let name = "youtubevideo"
public func parse(text: String) -> Plugin? {
guard let url = parseURL(text) else {
return nil
}
return YoutubePlugin(url: url)
}
public init() {
}
}
应使name
值与插件使用的链接文本匹配。由于YouTube插件看起来像[youtubevideo](https://youtu.be/kkdBB1hVLX0)
,因此使用youtubevideo
作为name
。
将原始链接地址传递给解析方法。你可以决定如何处理文本值来初始化你的插件,但也为插件提供了便利方法来简化此过程。
splitPluginParams
方法支持以下格式的多个插件参数在链接地址中。
param1::value1|param2::value2|param3::value3
splitPluginParams
将将这种格式的参数拆分为一个字典,您可以将其用于初始化您的插件。
假设一个 YouTube 插件看起来像这样
[youtubevideo](source::https://youtu.be/kkdBB1hVLX0||caption::Checkout this video)
然后,插件将更新为以下样子
public struct YoutubePlugin: Plugin {
public static let pluginName: PluginName = "youtubevideo"
public let url: URL
public let caption: String?
public var videoId: String? {
return url.path.components(separatedBy: "/").last
}
public init(url: URL, caption: String?) {
self.url = url
self.caption = caption
}
}
而 PluginParser 可能看起来如下
public struct YoutubePluginParser: PluginParser {
public let name = "youtubevideo"
public func parse(text: String) -> Plugin? {
let parameters = splitPluginParams(text)
guard parameters.count > 0,
let source = parameters["source"],
let url = URL(string: source) else {
return nil
}
let caption = parameters["caption"]
return YoutubePlugin(url: url, caption: caption)
}
public init() {
}
}
注册插件
在 Maaku 解析器使用之前,必须使用 PluginManager
注册 PluginParser。如果您不注册插件,它将作为链接或文本而非插件出现。
要注册 PluginParser,先对其进行初始化,并将其传递给 PluginManager.registerParsers
。
对于 YouTube 例子
PluginManager.registerParsers([YoutubePluginParser()])