commonmark4cj 库
flowchart LR
md[/MarkdownText/] -->parser(Parser解析)
parser --> node[Node树]
parser <--> BlockParser[[自定义段落解析]]
parser <--> DelimiterProcessor[[自定义行内符号解析]]
parser <--> InlineContentParser[[自定义行内解析]]
parser <--> LinkProcessor[[自定义行内解析]]
parser <--> PostProcessor[[后处理]]
node --> renderer(Renderer渲染)
renderer <--> visitor[[Visitor遍历]]
renderer --> res[/渲染结果/]
介绍
根据CommonMark规范(以及一些扩展)解析和呈现Markdown文本。
1 Node
前置条件:NA
场景:markdown解析得到的节点树,不同类型节点为不同的Node子类
约束:NA
可靠性:NA
1.1 普通Node 通常为行内节点
1.1.1 主要接口
/**
* 通用节点
*/
public abstract class Node <: ToString & Equatable<Node> {
/*
* 添加操作行为
* 参数 Visitor - 具体的操作行为
*/
public open func accept(visitor: Visitor): Unit
/*
* 获取下一个节点
* 返回值 ?Node - Option Node 下一个节点
*/
public func getNext(): ?Node
/*
* 获取上一个节点
* 返回值 ?Node - Option Node 上一个节点
*/
public func getPrevious(): ?Node
/*
* 获取第一个孩子节点
* 返回值 ?Node - Option Node 第一个孩子节点
*/
public func getFirstChild(): ?Node
/*
* 获取最后一个孩子节点
* 返回值 ?Node - Option Node 最后一个孩子节点
*/
public func getLastChild(): ?Node
/*
* 获取父节点
* 返回值 ?Node - Option Node 父节点
*/
public open func getParent(): ?Node
/*
* 末尾添加子节点
* 参数 Node - 子节点
*/
public func appendChild(child: Node): Unit
/*
* 开头添加子节点
* 参数 Node - 子节点
*/
public func prependChild(child: Node): Unit
/*
* 断开连接
*/
public func unlink(): Unit
/*
* 后插入一个兄弟节点
* 参数 Node - 兄弟节点
*/
public func insertAfter(sibling: Node): Unit
/*
* 前插入一个兄弟节点
* 参数 Node - 兄弟节点
*/
public func insertBefore(sibling: Node): Unit
/*
* toString
* 返回值 String - toString
*/
public open func toString(): String
/*
* 重写 ==
* 参数 Node - 比较Node
* 返回值 Bool - 是否相等
*/
public operator func ==(other: Node): Bool
/*
* 重写 !=
* 参数 Node - 比较Node
* 返回值 Bool - 是否相等
*/
public operator func !=(other: Node): Bool
}
/**
* 文本节点
*/
public class Text <: Node & Equatable<Text> {
public init(literal: String): Unit
/*
* 添加操作行为
* 参数 Visitor - 具体的操作行为
*/
public override func accept(visitor: Visitor): Unit
/*
* 获取文本
* 返回值 String - 文本
*/
public func getLiteral(): String
/*
* 设置文本
* 参数 String - 文本
*/
public func setLiteral(literal: String): Unit
/*
* 重写 ==
* 参数 Text - 比较Text
* 返回值 Bool - 是否相等
*/
public operator func ==(other: Text): Bool
/*
* 重写 !=
* 参数 Text - 比较Text
* 返回值 Bool - 是否相等
*/
public operator func !=(other: Text): Bool
}
/**
* HtmlInline节点
*/
public class HtmlInline <: Node {
public init(literal: String): Unit
/*
* 添加操作行为
* 参数 Visitor - 具体的操作行为
*/
public override func accept(visitor: Visitor): Unit
/*
* 获取文本
* 返回值 String - 文本
*/
public func getLiteral(): String
/*
* 设置文本
* 参数 String - 文本
*/
public func setLiteral(literal: String): Unit
}
/**
* CustomNode节点
*/
public abstract class CustomNode <: Node {
/*
* 添加操作行为
* 参数 Visitor - 具体的操作行为
*/
public override func accept(visitor: Visitor): Unit
}
/**
* 图片节点
*/
public class Image <: Node {
/*
* 初始化
* 参数 String - 图片地址链接
* 参数 ?String - 标题
*/
public init(destination: String, title: ?String)
/*
* 添加操作行为
* 参数 Visitor - 具体的操作行为
*/
public override func accept(visitor: Visitor): Unit
/*
* 获取地址链接
* 返回值 String - 地址链接
*/
public func getDestination(): String
/*
* 设置地址链接
* 参数 String - 地址链接
*/
public func setDestination(destination: String): Unit
/*
* 获取标题
* 返回值 ?String - 标题
*/
public func getTitle(): ?String
/*
* 设置标题
* 参数 String - 标题
*/
public func setTitle(title: String): Unit
}
/**
* Code节点
*/
public class Code <: Node {
public init(literal: String): Unit
/*
* 添加操作行为
* 参数 Visitor - 具体的操作行为
*/
public override func accept(visitor: Visitor): Unit
/*
* 获取文本
* 返回值 String - 文本
*/
public func getLiteral(): String
/*
* 设置文本
* 参数 String - 文本
*/
public func setLiteral(literal: String): Unit
}
/**
* HardLineBreak节点
*/
public class HardLineBreak <: Node {
/*
* 添加操作行为
* 参数 Visitor - 具体的操作行为
*/
public override func accept(visitor: Visitor): Unit
}
/**
* SoftLineBreak节点
*/
public class SoftLineBreak <: Node {
/*
* 添加操作行为
* 参数 Visitor - 具体的操作行为
*/
public override func accept(visitor: Visitor): Unit
}
/**
* Link节点
*/
public class Link <: Node {
public init(destination: String, title: ?String)
/*
* 添加操作行为
* 参数 Visitor - 具体的操作行为
*/
public override func accept(visitor: Visitor): Unit
/*
* 获取目标地址
* 返回值 String - 目标地址
*/
public func getDestination(): String
/*
* 设置目标地址
* 参数 String - 目标地址
*/
public func setDestination(destination: String): Unit
/*
* 获取标题
* 返回值 ?String - 标题
*/
public func getTitle(): ?String
/*
* 设置标题
* 参数 String - 标题
*/
public func setTitle(title: String): Unit
}
/**
* 分隔符接口
*/
public interface Delimited {
/*
* 获取开头分隔符
* 返回值 ?String - 标题
*/
func getOpeningDelimiter(): ?String
/*
* 获取结尾分隔符
* 返回值 ?String - 标题
*/
func getClosingDelimiter(): ?String
}
/**
* StrongEmphasis节点
*/
public class StrongEmphasis <: Node & Delimited {
/*
* 初始化
*/
public init(): Unit
/*
* 初始化
* 参数 String - 分隔符
*/
public init(delimiter: String): Unit
/*
* 设置分隔符
* 参数 String - 分隔符
*/
public func setDelimiter(delimiter: String): Unit
/*
* 获取开头分隔符
* 返回值 ?String - 标题
*/
public func getOpeningDelimiter(): ?String
/*
* 获取结尾分隔符
* 返回值 ?String - 标题
*/
public func getClosingDelimiter(): ?String
/*
* 添加操作行为
* 参数 Visitor - 具体的操作行为
*/
public override func accept(visitor: Visitor): Unit
}
public class Emphasis <: Node & Delimited {
/*
* 初始化
*/
public init()
/*
* 初始化
* 参数 String - 分隔符
*/
public init(delimiter: String)
/*
* 设置分隔符
* 参数 String - 分隔符
*/
public func setDelimiter(delimiter: String): Unit
/*
* 获取开头分隔符
* 返回值 ?String - 标题
*/
public func getOpeningDelimiter(): ?String
/*
* 获取结尾分隔符
* 返回值 ?String - 标题
*/
public func getClosingDelimiter(): ?String
/*
* 添加操作行为
* 参数 Visitor - 具体的操作行为
*/
public override func accept(visitor: Visitor): Unit
}
1.2 Block系列节点
1.2.1 主要接口
public abstract class Block <: Node {
/*
* 获取父节点
* 返回值 ?Node - Option Node 父节点
*/
public func getParent(): ?Node
/*
* 设置父节点
* 参数 Node - 父节点
*/
protected override func setParent(parent: Node): Unit
}
public class BlockQuote <: Block {
/*
* 添加操作行为
* 参数 Visitor - 具体的操作行为
*/
public func accept(visitor: Visitor): Unit
}
public class HtmlBlock <: Block {
/*
* 添加操作行为
* 参数 Visitor - 具体的操作行为
*/
public func accept(visitor: Visitor): Unit
/*
* 获取文本
* 返回值 String - 文本
*/
public func getLiteral(): String
/*
* 设置文本
* 参数 String - 文本
*/
public func setLiteral(literal: String): Unit
}
public abstract class CustomBlock <: Block {
/*
* 添加操作行为
* 参数 Visitor - 具体的操作行为
*/
public func accept(visitor: Visitor): Unit
}
public class ThematicBreak <: Block {
/*
* 添加操作行为
* 参数 Visitor - 具体的操作行为
*/
public func accept(visitor: Visitor): Unit
}
public class Document <: Block {
/*
* 添加操作行为
* 参数 Visitor - 具体的操作行为
*/
public func accept(visitor: Visitor): Unit
}
public class Paragraph <: Block {
/*
* 添加操作行为
* 参数 Visitor - 具体的操作行为
*/
public func accept(visitor: Visitor): Unit
}
public class IndentedCodeBlock <: Block {
/*
* 添加操作行为
* 参数 Visitor - 具体的操作行为
*/
public func accept(visitor: Visitor): Unit
/*
* 获取文本
* 返回值 String - 文本
*/
public func getLiteral(): String
/*
* 设置文本
* 参数 String - 文本
*/
public func setLiteral(literal: String): Unit
}
public class FencedCodeBlock <: Block {
/*
* 添加操作行为
* 参数 Visitor - 具体的操作行为
*/
public func accept(visitor: Visitor): Unit
/*
* 获取围栏字符 默认`
* 返回值 Rune - 围栏字符
*/
public func getFenceChar(): Rune
/*
* 设置围栏字符
* 参数 Rune - 围栏字符
*/
public func setFenceChar(fenceChar: Rune): Unit
/*
* 获取围栏代码块长度 至少3
* 返回值 Int64 - 围栏代码块长度
*/
public func getFenceLength(): Int64
/*
* 设置围栏代码块长度
* 参数 Int64 - 围栏代码块长度
*/
public func setFenceLength(fenceLength: Int64): Unit
/*
* 获取围栏与代码块的缩进量
* 返回值 Int64 - 围栏与代码块的缩进量
*/
public func getFenceIndent(): Int64
/*
* 设置围栏与代码块的缩进量
* 参数 Int64 - 围栏与代码块的缩进量
*/
public func setFenceIndent(fenceIndent: Int64): Unit
/*
* 获取语言标识符
* 返回值 String - 语言标识符
*/
public func getInfo(): String
/*
* 设置语言标识符
* 参数 String - 语言标识符
*/
public func setInfo(info: String): Unit
/*
* 获取文本
* 返回值 String - 文本
*/
public func getLiteral(): String
/*
* 设置文本
* 参数 String - 文本
*/
public func setLiteral(literal: String): Unit
}
public class ListItem <: Block {
/*
* 添加操作行为
* 参数 Visitor - 具体的操作行为
*/
public func accept(visitor: Visitor): Unit
}
public class Heading <: Block {
/*
* 添加操作行为
* 参数 Visitor - 具体的操作行为
*/
public func accept(visitor: Visitor): Unit
/*
* 获取标题级别
* 返回值 Int64 - 标题级别
*/
public func getLevel(): Int64
/*
* 设置标题级别
* 参数 Int64 - 标题级别
*/
public func setLevel(level: Int64): Unit
}
/**
* 列表块节点
*/
public abstract class ListBlock <: Block {
/*
* 获取表块是不是紧凑的
* 返回值 Bool - 列表块是不是紧凑的
*/
public func isTight(): Bool
/*
* 设置列表块是不是紧凑的
* 参数 Bool - 列表块是不是紧凑的
*/
public func setTight(tight: Bool)
}
/**
* 无序列表块节点
*/
public class BulletList <: ListBlock {
/*
* 初始化
* 参数 Rune - 标记
*/
public init(bulletMarker: Rune): Unit
/*
* 添加操作行为
* 参数 Visitor - 具体的操作行为
*/
public func accept(visitor: Visitor): Unit
/*
* 获取标记
* 返回值 Rune - 标记
*/
public func getBulletMarker(): Rune
/*
* 设置标记
* 参数 Rune - 标记
*/
public func setBulletMarker(bulletMarker: Rune): Unit
}
/**
* 有序列表块节点
*/
public class OrderedList <: ListBlock {
/*
* 初始化
* 参数 Int64 - 起始数字
* 参数 Rune - 分隔符
*/
public init(startNumber: Int64, delimiter: Rune)
/*
* 添加操作行为
* 参数 Visitor - 具体的操作行为
*/
public func accept(visitor: Visitor): Unit
/*
* 获取起始数字
* 返回值 Int64 - 起始数字
*/
public func getStartNumber(): Int64
/*
* 设置起始数字
* 参数 Int64 - 起始数字
*/
public func setStartNumber(startNumber: Int64): Unit
/*
* 获取分隔符
* 返回值 Rune - 分隔符
*/
public func getDelimiter(): Rune
/*
* 设置分隔符
* 参数 Rune - 分隔符
*/
public func setDelimiter(delimiter: Rune): Unit
}
/**
* LinkReferenceDefinition节点
*/
public class LinkReferenceDefinition <: Block {
/*
* 初始化
*/
public init()
/*
* 添加操作行为
* 参数 String - 链接引用的标签
* 参数 String - 目标地址
* 参数 String - 标题
*/
public init(label: String, destination: String, title: String)
/*
* 获取链接引用的标签
* 返回值 ?String - 链接引用的标签
*/
public func getLabel(): ?String
/*
* 设置链接引用的标签
* 参数 String - 链接引用的标签
*/
public func setLabel(label: String): Unit
/*
* 获取目标地址
* 返回值 String - 目标地址
*/
public func getDestination(): String
/*
* 设置目标地址
* 参数 String - 目标地址
*/
public func setDestination(destination: String): Unit
/*
* 获取标题
* 返回值 ?String - 标题
*/
public func getTitle(): ?String
/*
* 设置标题
* 参数 String - 标题
*/
public func setTitle(title: String): Unit
/*
* 添加操作行为
* 参数 Visitor - 具体的操作行为
*/
public override func accept(visitor: Visitor): Unit
}
1.2.2 示例
import commonmark4cj.commonmark.*
@TestCase
func documentTest(): Unit {
var document: Document = Document ()
var paragraph = Paragraph()
var blockQuote = BlockQuote()
var htmlBlock = HtmlBlock()
var thematicBreak = ThematicBreak()
var indentedCodeBlock = IndentedCodeBlock()
var text = Text("text")
document.appendChild(paragraph)
document.appendChild(blockQuote)
document.appendChild(htmlBlock)
document.appendChild(thematicBreak)
document.appendChild(indentedCodeBlock)
document.appendChild(text)
assertEquals("Document{}", blockQuote.getParent()().toString())
assertEquals(None, document.getParent())
htmlBlock.setLiteral("p1")
assertEquals("p1", htmlBlock.getLiteral())
assertEquals("HtmlBlock{}", thematicBreak.getPrevious()().toString())
htmlBlock.insertBefore(Text("foo"))
assertEquals("Text{literal=foo}", htmlBlock.getPrevious()().toString())
assertEquals(true, paragraph != htmlBlock)
}
1.3 Visitor接口
1.3.1 主要接口
public interface Visitor {
func visit(blockQuote: BlockQuote): Unit
func visit(bulletList: BulletList): Unit
func visit(code: Code): Unit
func visit(document: Document): Unit
func visit(emphasis: Emphasis): Unit
func visit(fencedCodeBlock: FencedCodeBlock): Unit
func visit(hardLineBreak: HardLineBreak): Unit
func visit(heading: Heading): Unit
func visit(thematicBreak: ThematicBreak): Unit
func visit(htmlInline: HtmlInline): Unit
func visit(htmlBlock: HtmlBlock): Unit
func visit(image: Image): Unit
func visit(indentedCodeBlock: IndentedCodeBlock): Unit
func visit(link: Link): Unit
func visit(listItem: ListItem): Unit
func visit(orderedList: OrderedList): Unit
func visit(paragraph: Paragraph): Unit
func visit(softLineBreak: SoftLineBreak): Unit
func visit(strongEmphasis: StrongEmphasis): Unit
func visit(text: Text): Unit
func visit(linkReferenceDefinition: LinkReferenceDefinition): Unit
func visit(customBlock: CustomBlock): Unit
func visit(customNode: CustomNode): Unit
}
public abstract class AbstractVisitor <: Visitor {
/*
* 处理渲染BlockQuote节点的行为
* 参数 BlockQuote - BlockQuote节点
*/
public open func visit(blockQuote: BlockQuote): Unit
/*
* 处理渲染BulletList节点的行为
* 参数 BulletList - BulletList节点
*/
public open func visit(bulletList: BulletList): Unit
/*
* 处理渲染Code节点的行为
* 参数 Code - Code节点
*/
public open func visit(code: Code): Unit
/*
* 处理渲染Document节点的行为
* 参数 Document - Document节点
*/
public open func visit(document: Document): Unit
/*
* 处理渲染Emphasis节点的行为
* 参数 Emphasis - Emphasis节点
*/
public open func visit(emphasis: Emphasis): Unit
/*
* 处理渲染FencedCodeBlock节点的行为
* 参数 FencedCodeBlock - FencedCodeBlock节点
*/
public open func visit(fencedCodeBlock: FencedCodeBlock): Unit
/*
* 处理渲染HardLineBreak节点的行为
* 参数 HardLineBreak - HardLineBreak节点
*/
public open func visit(hardLineBreak: HardLineBreak): Unit
/*
* 处理渲染Heading节点的行为
* 参数 Heading - Heading节点
*/
public open func visit(heading: Heading): Unit
/*
* 处理渲染ThematicBreak节点的行为
* 参数 ThematicBreak - ThematicBreak节点
*/
public open func visit(thematicBreak: ThematicBreak): Unit
/*
* 处理渲染HtmlInline节点的行为
* 参数 HtmlInline - HtmlInline节点
*/
public open func visit(htmlInline: HtmlInline): Unit
/*
* 处理渲染HtmlBlock节点的行为
* 参数 HtmlBlock - HtmlBlock节点
*/
public open func visit(htmlBlock: HtmlBlock): Unit
/*
* 处理渲染Image节点的行为
* 参数 Image - Image节点
*/
public open func visit(image: Image): Unit
/*
* 处理渲染IndentedCodeBlock节点的行为
* 参数 IndentedCodeBlock - IndentedCodeBlock节点
*/
public open func visit(indentedCodeBlock: IndentedCodeBlock): Unit
/*
* 处理渲染Link节点的行为
* 参数 Link - Link节点
*/
public open func visit(link: Link): Unit
/*
* 处理渲染ListItem节点的行为
* 参数 ListItem - ListItem节点
*/
public open func visit(listItem: ListItem): Unit
/*
* 处理渲染OrderedList节点的行为
* 参数 OrderedList - OrderedList节点
*/
public open func visit(orderedList: OrderedList): Unit
/*
* 处理渲染Paragraph节点的行为
* 参数 Paragraph - Paragraph节点
*/
public open func visit(paragraph: Paragraph): Unit
/*
* 处理渲染SoftLineBreak节点的行为
* 参数 SoftLineBreak - SoftLineBreak节点
*/
public open func visit(softLineBreak: SoftLineBreak): Unit
/*
* 处理渲染StrongEmphasis节点的行为
* 参数 StrongEmphasis - StrongEmphasis节点
*/
public open func visit(strongEmphasis: StrongEmphasis): Unit
/*
* 处理渲染Text节点的行为
* 参数 Text - Text节点
*/
public open func visit(text: Text): Unit
/*
* 处理渲染LinkReferenceDefinition节点的行为
* 参数 LinkReferenceDefinition - LinkReferenceDefinition节点
*/
public open func visit(linkReferenceDefinition: LinkReferenceDefinition): Unit
/*
* 处理渲染CustomBlock节点的行为
* 参数 CustomBlock - CustomBlock节点
*/
public open func visit(customBlock: CustomBlock): Unit
/*
* 处理渲染CustomNode节点的行为
* 参数 CustomNode - CustomNode节点
*/
public open func visit(customNode: CustomNode): Unit
}
1.3.2 示例
import commonmark4cj.commonmark.*
@TestCase
func test_Text_accept():Unit {
var text = Text("aa")
text.appendChild(Text("bb"))
text.accept(AbstractVisitorImpl())
@Assert(text.getLiteral(),"aa")
let firstChild = text.getFirstChild().getOrThrow()
@Assert((firstChild as Text).getOrThrow().getLiteral(),"bb")
let lastChild = text.getLastChild().getOrThrow()
@Assert((lastChild as Text).getOrThrow().getLiteral(),"cc")
}
class AbstractVisitorImpl <: AbstractVisitor {
public func visit(text: Text): Unit {
text.appendChild(Text("cc"))
}
}
2 Parse
前置条件:NA
将markdown文本解析为节点树 约束:NA
可靠性:NA
2.1 Parser
2.1.1 主要接口
public class Parser {
/*
* 获取ParserBuilder对象
* 返回值 ParserBuilder - ParserBuilder对象
*/
public static func builder(): ParserBuilder
/*
* 解析文本 生成Node
* 参数 String - 文本
* 返回值 Node - Node对象
*/
public func parse(input: String): Node
/*
* 解析流 生成Node
* 参数 StringReader<InputStream> - 流
* 返回值 Node - Node对象
*/
public func parseReader(input: StringReader<InputStream>): Node
}
public class ParserBuilder {
/*
* 构建parse对象
* 返回值 Parser - Parser对象
*/
public func build(): Parser
/*
* 获取那七个block相关node对象象集合
* 返回值 HashSet<String> - 那七个block相关Node的对象集合
*/
public func getEnabledBlockTypes(): HashSet<String>
/*
* 作为插件 拓展解析器 参考table
* 参数 Iterable<T> - 拓展的解析器集合
* 返回值 ParserBuilder - ParserBuilder对象
*/
public func extensions<T>(extensions: Iterable<T>): ParserBuilder where T <: Extension
/*
* 更新支持解析的Node对象集合
* 参数 HashSet<String> - 支持解析的Node对象集合
* 返回值 ParserBuilder - ParserBuilder对象
*/
public func enabledBlockTypes(enabledBlockTypes: HashSet<String>): ParserBuilder
/*
* 增加用户新增的解析工厂类
* 参数 BlockParserFactory - 解析工厂类
* 返回值 ParserBuilder - ParserBuilder对象
*/
public func customBlockParserFactory(blockParserFactory: BlockParserFactory): ParserBuilder
/*
* 增加用户新增的分隔符处理器
* 参数 DelimiterProcessor - DelimiterProcessor
* 返回值 ParserBuilder - ParserBuilder对象
*/
public func customDelimiterProcessor(delimiterProcessor: DelimiterProcessor): ParserBuilder
/*
* 增加用户新增的PostProcessor处理器
* 参数 PostProcessor - PostProcessor
* 返回值 ParserBuilder - ParserBuilder对象
*/
public func postProcessor(postProcessor: PostProcessor): ParserBuilder
/*
* 实现InlineParser接口 用户自定义行内解析
* 参数 InlineParserFactory - 用户覆盖的InlineParserFactory子类
* 返回值 ParserBuilder - ParserBuilder对象
*/
public func inlineParserFactory(inlineParserFactory: InlineParserFactory): ParserBuilder
/*
* 获取InlineParserFactory 没有定义就生成默认的InlineParserImpl类
* 返回值 ParserBuilder - ParserBuilder对象
*/
public func getInlineParserFactory(): InlineParserFactory
}
public interface ParserExtension <: Extension {
/*
* 插件拓展 生成拓展的插件
* 参数 ParserBuilder - ParserBuilder
*/
func ext(parserBuilder: ParserBuilder): Unit
}
/*
* 后处理
*/
public interface PostProcessor {
/*
* 解析Node
* 参数 Node - Node
* 返回值 Node - Node
*/
func process(node: Node): Node
}
2.1.2 示例
import commonmark4cj.commonmark.*
@TestCase
func parse_test():Unit {
let given: String = "# heading 1\n\nnot a heading"
var parser: Parser = Parser.builder().build()
var document: Node = parser.parse(given)
assertEquals("Heading{}", document.getFirstChild()().toString())
}
2.2 BlockParser
2.2.1 主要接口
public interface BlockParser {
/*
* 是否可以包含其他块级元素
* 返回值 Bool - Bool
*/
func isContainer(): Bool
/*
* 是否可以懒惰的换行
* 返回值 Bool - Bool
*/
func canHaveLazyContinuationLines(): Bool
/*
* 是否可以包含这个Block对象
* 参数 Block - Block对象
* 返回值 Bool - Bool
*/
func canContain(childBlock: Block): Bool
/*
* 获取Block对象
* 返回值 Block - block对象
*/
func getBlock(): Block
/*
* 获取跨行元素对象 如果存在
* 参数 ParserState - ParserState对象
* 返回值 Option<BlockContinue> - BlockContinue
*/
func tryContinue(parserState: ParserState): Option<BlockContinue>
/*
* 添加一行
* 参数 line - 行数据
*/
func addLine(line: SourceLine): Unit
/**
* 向当前解析的块添加一个源范围。在{@link AbstractBlockParser}中的默认实现是将其添加到块中。除非您有复杂的解析需求,需要检查源位置,否则无需覆盖此方法。
*/
func addSourceSpan(sourceSpan: SourceSpan): Unit
/**
* 返回此解析器解析的定义。此处返回的定义稍后可通过内联解析过程中的{@link InlineParserContext#getDefinition}进行访问。
*/
func getDefinitions(): ArrayList<LinkReferenceDefinition>
/*
* 关闭块对象
*/
func closeBlock(): Unit
/*
* 使用InlineParser解析文本
* 参数 InlineParser - InlineParser对象
*/
func parseInlines(inlineParser: InlineParser): Unit
}
public open class BlockContinue {
/*
* 清空BlockContinue对象
* 返回值 InlineParser - Option<BlockContinue>.None
*/
public static func none(): Option<BlockContinue>
/*
* 设置跨行的元素起始下标
* 参数 Int64 - 起始下标
* 返回值 BlockContinue - 构建跨行元素实现类
*/
public static func atIndex(newIndex: Int64): BlockContinue
/*
* 设置跨行的元素起始下标
* 参数 Int64 - 起始下标
* 返回值 BlockContinue - 构建跨行元素实现类
*/
public static func atColumn(newColumn: Int64): BlockContinue
/*
* 结束跨行
* 返回值 BlockContinue - 构建跨行元素实现类
*/
public static func finished(): BlockContinue
}
public interface BlockParserFactory {
/*
* 初始化一个特定的 BlockParser 实例来解析当前的文本行
* 参数 ParserState - ParserState 对象
* 参数 MatchedBlockParser - MatchedBlockParser 对象
* 返回值 Option<BlockStart> - BlockStart
*/
func tryStart(state: ParserState, matchedBlockParser: MatchedBlockParser): Option<BlockStart>
}
public abstract class BlockStart {
/*
* 生成一个 Option<BlockStart>.None实例
* 返回值 Option<BlockStart> - Option<BlockStart>.None
*/
public static func none(): Option<BlockStart>
/*
* 生成默认的BlockStart实现类
* 参数 Array<AbstractBlockParser> - 解析类数组
* 返回值 BlockStart - BlockStart实现类
*/
public static func of(blockParsers: Array<AbstractBlockParser>): BlockStart
/*
* 指定下标
* 参数 Int64 - index
* 返回值 BlockStart - BlockStart实现类
*/
public func atIndex(newIndex: Int64): BlockStart
/*
* 指定下标
* 参数 Int64 - column
* 返回值 BlockStart - BlockStart实现类
*/
public func atColumn(newColumn: Int64): BlockStart
/*
* 是否可替换当前解析类
* 返回值 BlockStart - BlockStart实现类
*/
public func replaceActiveBlockParser(): BlockStart
}
public interface ParserState {
/*
* 获取当前行内容
* 返回值 SourceLine - 内容
*/
func getLine(): SourceLine
func getNextLine(): String
/*
* 获取下标
* 返回值 Int64 - 下标
*/
func getIndex(): Int64
/*
* 获取下一个没有空格的下标
* 返回值 Int64 - 下标
*/
func getNextNonSpaceIndex(): Int64
/*
* 获取下标
* 返回值 Int64 - 下标
*/
func getColumn(): Int64
/*
* 获取缩进级别
* 返回值 Int64 - 缩进级别
*/
func getIndent(): Int64
/*
* 是否是空行
* 返回值 Bool - 是否是空行
*/
func isBlank(): Bool
/*
* 获取最底层的块块解析对象
* 返回值 AbstractBlockParser - 最底层的块块解析对象
*/
func getActiveBlockParser(): AbstractBlockParser
}
/**
* 来自输入源的一组行({@link SourceLine})。
*/
public class SourceLines {
public static func empty(): SourceLines
public static func of(sourceLine: SourceLine): SourceLines
public static func of(sourceLines: ArrayList<SourceLine>): SourceLines
public func addLine(sourceLine: SourceLine): Unit
public func getLines(): ArrayList<SourceLine>
public func isEmpty(): Bool
public func getContent(): String
public func getSourceSpans(): ArrayList<SourceSpan>
}
/**
* 来自输入源的一行或一行的一部分。
*/
public class SourceLine {
public static func of(content: String, sourceSpan: ?SourceSpan): SourceLine
public func getContent(): String
public func getSourceSpan(): ?SourceSpan
public func substring(beginIndex: Int, endIndex: Int): SourceLine
}
/**
* 源输入中的一段文本。
* 如果一个Block有多行,则每行都会有一个SourceSpan
*/
public class SourceSpan <: Equatable<SourceSpan> & ToString {
public static func of(line: Int, col: Int, input: Int, length: Int): SourceSpan
/**
* @return 从 0 开始的行索引,例如 0 表示第一行,1 表示第二行,以此类推
*/
public func getLineIndex(): Int
/**
* @return 从 0 开始的列(行内字符)索引,例如 0 表示行的第一个字符,1 表示第二个字符,以此类推
*/
public func getColumnIndex(): Int
/**
* @return 在整个输入中从 0 开始的索引
*/
public func getInputIndex(): Int
/**
* @return 跨度的字符长度
*/
public func getLength(): Int
/**
* 截取
*/
public func subSpan(beginIndex: Int): SourceSpan
/**
* 截取
*/
public func subSpan(beginIndex: Int, endIndex: Int): SourceSpan
public operator func ==(that: SourceSpan): Bool
public func toString(): String
}
/**
* 可添加的源跨度列表。负责合并相邻的源跨度。
*/
public class SourceSpans {
public static func empty(): SourceSpans
public func getSourceSpans(): ArrayList<SourceSpan>
public func addAllFrom(nodes: Collection<Node>): Unit
public func addAllFromTexts(nodes: ReadOnlyList<Text>): Unit
public func addAll(other: ReadOnlyList<SourceSpan>): Unit
}
/**
* {@link Scanner} 内的源位置。此类型有意保持不透明,以免暴露 Scanner 的内部结构。
*/
public class SourcePosition <: ToString {
public SourcePosition(public let lineIndex: Int, public let index: Int)
public func toString(): String
}
/**
* 是否在解析时包含 {@link SourceSpan},
* 参见 {@link ParserBuilder#includeSourceSpans(IncludeSourceSpans)}。
*/
public enum IncludeSourceSpans <: Equatable<IncludeSourceSpans> {
/**
* 不包含源跨度。
*/
| NONE
/**
* 在 Block 节点上包含源跨度。
*/
| BLOCKS
/**
* 在块节点和行内节点上都包含源跨度。
*/
| BLOCKS_AND_INLINES
public operator func ==(other: IncludeSourceSpans): Bool
}
/**
* 解析时的文本扫描器
*/
public class Scanner {
public static func of(lines: SourceLines): Scanner
public func peekRune(): Rune
public func peekLine(): String
public func peek(): Byte
public func peekPrev(): Byte
public func peekCodePoint(): Rune
public func peekPreviousCodePoint(): Rune
public func hasNext(): Bool
/**
* 下一个字符
*/
public func next(): Unit
/**
* 检查下一个字节 并前进
*/
public func next(b: Byte): Bool
/**
* 检查指定的 Rune 是否为下一个字符并前进位置。
*
* @param c 要检查的 Rune(包括换行符)
* @return 如果匹配且位置已前进则返回 true,否则返回 false
*/
public func nextRune(c: Rune): Bool
/**
* 检查当前行是否具有指定内容并前进位置。注意,如果要匹配换行符,请使用 {@link #next(Rune)}。
*
* @param content 要在单行上匹配的文本内容(不包括换行符)
* @return 如果匹配且位置已前进则返回 true,否则返回 false
*/
public func next(content: String): Bool
public func matchMultipleRune(c: Rune): Int
public func matchMultiple(b: Byte): Int
public func matches(matcher: CharMatcher): Int
/**
* 跳过空格
* @return 跳过空格数量
*/
public func whitespace(): Int
public func find(c: Byte): Int
public func find(matcher: CharMatcher): Int
// 不暴露 Int 索引,因为将来我们可能希望将输入切换为行的 Collection<String>,而不是一个连续的 String。
public func position(): SourcePosition
public func setPosition(position: SourcePosition): Unit
// 对于调用者将结果追加到 StringBuilder 的情况,我们可以提供另一个方法来避免一些不必要的复制。
public func getSource(begin: SourcePosition, end: SourcePosition): SourceLines
}
2.2.2 示例
import commonmark4cj.commonmark.*
main(): Int64 {
let parser: Parser = Parser.builder().customBlockParserFactory(DashBlockParserFactory()).build()
let document: Node = parser.parse("hey\n\n---\n")
println(document.getFirstChild().getOrThrow().toString())
println((document.getFirstChild().getOrThrow().getFirstChild().getOrThrow() as Text).getOrThrow().getLiteral())
println(document.getLastChild().getOrThrow().toString())
return 0
}
class DashBlockParserFactory <: AbstractBlockParserFactory {
public override func tryStart(state: ParserState, matchedBlockParser: MatchedBlockParser): ?BlockStart {
if (state.getLine() == ("---")) {
return BlockStart.of(DashBlockParser())
}
return BlockStart.none()
}
}
class DashBlock <: CustomBlock {
public func getNodeType(): NodeType {
"DashBlock"
}
}
class DashBlockParser <: AbstractBlockParser {
private var dash: DashBlock = DashBlock()
public override func getBlock(): Block {
return dash
}
public override func tryContinue(parserState: ParserState): ?BlockContinue {
return BlockContinue.none()
}
}
2.3 InlineParser
2.3.1 主要接口
public interface InlineParser {
/*
* 解析行内元素
* 参数 String - 文本
* 返回值 Node - 与生成的Node互为父子节点
*/
func parse(input: String, node: Node): Unit
}
public interface InlineParserContext {
/*
* 获取用户自定义的分割符处理器
* 返回值 ArrayList<DelimiterProcessor> - ArrayList<DelimiterProcessor>
*/
func getCustomDelimiterProcessors(): ArrayList<DelimiterProcessor>
/**
* 获取用户自定义的分割符处理器factory
*/
func getCustomInlineContentParserFactories(): ArrayList<InlineContentParserFactory>
/*
* 根据名字获取对应的链接引用
* 参数 String - 名字
* 返回值 ?LinkReferenceDefinition - ?LinkReferenceDefinition
*/
func getLinkReferenceDefinition(label: String): ?LinkReferenceDefinition
/**
* 获取用户自定义的链接处理器
*/
func getCustomLinkProcessors(): ArrayList<LinkProcessor>
/**
* 获取用户自定义的链接标志符
*/
func getCustomLinkMarkers(): HashSet<Rune>
/**
* 根据标签(label)查找类型定义
*/
func getDefinition(typ: String, label: String): ?LinkReferenceDefinition
}
public interface InlineParserFactory {
/*
* 构建InlineParser行内解析器实例
* 参数 InlineParserContext - InlineParserContext
* 返回值 InlineParser - InlineParser对象
*/
func create(inlineParserContext: InlineParserContext): InlineParser
}
public interface DelimiterProcessor {
/*
* 获取开始分隔符
* 返回值 Rune - 开始分隔符
*/
func getOpeningCharacter(): Rune
/*
* 获取结束分隔符
* 返回值 Rune - 结束分隔符
*/
func getClosingCharacter(): Rune
/*
* 获取最小长度 为1
* 返回值 Int64
*/
func getMinLength(): Int64
/*
* 处理行内元素
* 参数 openingRun - 包含开始符号的文本节点
* 参数 closingRun - 包含结束符号的文本节点
* 返回值 使用了多少分隔符
*/
func process(openingRun: DelimiterRun, closingRun: DelimiterRun): Int
}
public interface DelimiterRun {
/*
* 是否可以开启一个新的分隔符
* 返回值 Bool - 是否可以打开
*/
func canOpen(): Bool
/*
* 是否可以关闭分隔符
* 返回值 Bool - 是否可以关闭
*/
func canClose(): Bool
/*
* 序列长度
* 返回值 Bool - 是否可以关闭
*/
func getLength(): Int64
/*
* 序列原始长度
* 返回值 Bool - 是否可以关闭
*/
func getOriginalLength(): Int64
/*
* 最内层的开始分隔符,例如对于 ***,即最后一个 *
*/
func getOpener(): Text
/*
* 最内层的结束分隔符,例如对于 ***,即第一个 *
*/
func getCloser(): Text
/*
* 获取指定长度的开始分隔符节点。长度必须在 1 到 length() 之间。
* 例如,对于分隔符序列 ***,传入 1 将返回最后一个 *,
* 传入 2 将返回倒数第二个 * 和最后一个 *。
*/
func getOpeners(length: Int): ReadOnlyList<Text>
/*
* 获取指定长度的结束分隔符节点。长度必须在 1 到 length() 之间。
* 例如,对于分隔符序列 ***,传入 1 将返回第一个 *,
* 传入 2 将返回第一个 * 和第二个 *。
*/
func getClosers(length: Int): ReadOnlyList<Text>
}
/*
* 行内内容解析器。通过 InlineContentParserFactory 注册,并由其 create 方法创建。
* 其生命周期与每个被解析的行内内容片段绑定,每次解析都会创建一个新的实例。
*/
public interface InlineContentParser {
/*
* 尝试从当前位置开始解析行内内容。注意当前位置的字符是创建此解析器的工厂的
* getTriggerCharacters() 之一。
* 对于正在解析的给定行内内容片段,此方法可以被多次调用:每次遇到触发字符时调用一次。
*
* 参数 inlineParserState - 行内解析器的当前状态
* 返回值 ParsedInline - 解析结果;可以表示此解析器不感兴趣,或解析成功
*/
func tryParse(inlineParserState: InlineParserState): ParsedInline
}
/*
* 用于扩展行内内容解析的工厂。
* 关于如何注册,请参见 ParserBuilder.customInlineContentParserFactory。
*/
public interface InlineContentParserFactory {
/*
* 行内内容解析器需要有一个特殊的"触发"字符来激活它。当在内联解析过程中遇到此字符时,
* 将使用当前解析器状态调用 InlineContentParser.tryParse。也可以注册多个触发字符。
*/
@Frozen
func getTriggerCharacters(): HashSet<Rune>
/*
* 创建一个执行解析的 InlineContentParser。对于块结构内的每个行内内容文本片段,
* create 方法会被调用一次,之后每次遇到触发字符时也会被调用。
*/
func create(): InlineContentParser
}
public interface InlineParserState {
/*
* 返回当前位置(位于行内解析器所注册的触发字符上)的输入扫描器。
* 注意,此方法始终返回同一个实例,如果需要回溯,请使用
* Scanner.position() 和 Scanner.setPosition(SourcePosition)。
*/
func getScanner(): Scanner
}
2.3.2 示例
import commonmark4cj.commonmark.*
@TestCase
public func inlineParser(): Unit {
let parser: Parser = Parser.builder().inlineParserFactory(fakeInlineParserFactory()).build()
let input: String = "**bold** **bold** ~~strikethrough~~"
assertEquals(parser.parse(input).getFirstChild()().getFirstChild()().toString(), "ThematicBreak{}")
}
class fakeInlineParser <: InlineParser {
public override func parse(input: String, node: Node): Unit {
node.appendChild(ThematicBreak())
}
}
class fakeInlineParserFactory <: InlineParserFactory {
public override func create(inlineParserContext: InlineParserContext): InlineParser {
return fakeInlineParser()
}
}
2.4 Strikethrough
2.4.1 主要接口
public abstract class StrikethroughNodeRenderer <: NodeRenderer {
/*
* 获取删除线类型
* 返回值 HashSet<String> - 删除线类型
*/
public override func getNodeTypes(): HashSet<String>
}
public class Strikethrough <: CustomNode & Delimited {
/*
* 构造函数
* 参数 delimiter 使用的分隔符
*/
public Strikethrough(let delimiter: String) {}
/*
* 获取起始分隔符
* 返回值 ?String> - 起始分隔符
*/
public override func getOpeningDelimiter(): ?String
/*
* 获取结束分隔符
* 返回值 ?String> - 结束分隔符
*/
public override func getClosingDelimiter(): ?String
}
public class StrikethroughExtension <: ParserExtension & HtmlRendererExtension & TextContentRendererExtension {
/*
* 拓展插件
* 返回值 Extension - Extension
*/
public static func create(): Extension
/*
* 插件拓展
* 参数 ParserBuilder - ParserBuilder
*/
public override func ext(parserBuilder: ParserBuilder): Unit
/*
* 插件拓展
* 参数 HtmlRendererBuilder - HtmlRendererBuilder
*/
public override func ext(rendererBuilder: HtmlRendererBuilder): Unit
/*
* 插件拓展
* 参数 TextContentRendererBuilder - TextContentRendererBuilder
*/
public override func ext(rendererBuilder: TextContentRendererBuilder): Unit
}
2.4.2 示例
import commonmark4cj.commonmark.*
@TestCase
public class StrikethroughTest {
private static let EXTENSIONS: Iterable<Extension> = ArrayList<Extension>(StrikethroughExtension.create())
private static let PARSER: Parser = Parser.builder().extensions(EXTENSIONS).build()
private static let HTML_RENDERER: HtmlRenderer = HtmlRenderer.builder().extensions(EXTENSIONS).build()
private static let CONTENT_RENDERER: TextContentRenderer = TextContentRenderer.builder()
.extensions(EXTENSIONS).build()
@TestCase
public func oneTildeIsNotEnough(): Unit {
assertRendering("~foo~", "<p>~foo~</p>\n")
}
func render(source: String): String {
return HTML_RENDERER.render(PARSER.parse(source))
}
func assertRendering(source: String, expectedResult: String): Unit {
let renderedContent: String = render(source)
let expected: String = showTabs(expectedResult + "\n\n" + source)
let actual: String = showTabs(renderedContent + "\n\n" + source)
assertEquals(expected, actual)
}
func showTabs(s: String): String {
return s.replace("\t", "\u{2192}")
}
}
2.5 Table
2.5.1 主要接口
public abstract class TableNodeRenderer <: NodeRenderer {
/*
* 获取表格类型
* 返回值 HashSet<String> - 表格类型
*/
public override func getNodeTypes(): HashSet<String>
/*
* 渲染
* 参数 Node - Node
*/
public override func render(node: Node): Unit
}
public class TableBlock <: CustomBlock {}
public class TableBody <: CustomNode {}
public class TableCell <: CustomNode {
/*
* 是不是表头
* 返回值 Bool - Bool
*/
public func isHeader(): Bool
/*
* 设置该行是表头
* 参数 Bool - Bool
*/
public func setHeader(header: Bool): Unit
/*
* 获取对齐方式
* 返回值 ?Alignment - 对齐方式
*/
public func getAlignment(): ?Alignment
/*
* 设置对齐方式
* 参数 Alignment - 对齐方式
*/
public func setAlignment(alignment: Alignment): Unit
}
public enum Alignment {
| LEFT
| CENTER
| RIGHT
}
public class TableHead <: CustomNode {}
public class TableRow <: CustomNode {}
public class TablesExtension <: ParserExtension & HtmlRendererExtension & TextContentRendererExtension {
/*
* 拓展插件
* 返回值 Extension - Extension
*/
public static func create(): Extension
/*
* 拓展插件
* 参数 ParserBuilder - ParserBuilder
*/
public func ext(parserBuilder: ParserBuilder): Unit
/*
* 拓展插件
* 参数 HtmlRendererBuilder - HtmlRendererBuilder
*/
public func ext(rendererBuilder: HtmlRendererBuilder): Unit
/*
* 拓展插件
* 参数 TextContentRendererBuilder - TextContentRendererBuilder
*/
public func ext(rendererBuilder: TextContentRendererBuilder): Unit
}
2.5.2 示例
import commonmark4cj.commonmark.*
@Test
public class TableTT {
@TestCase
func mustHaveHeaderAndSeparator(): Unit {
let tt: TablesTest = TablesTest()
@PowerAssert(tt.assertRendering("Abc|Def", "<p>Abc|Def</p>\n") == true)
@PowerAssert(tt.assertRendering("Abc | Def", "<p>Abc | Def</p>\n") == true)
}
}
public abstract class RenderingTestCase {
protected func render(source: String): String
public func assertRendering(source: String, expectedResult: String): Bool {
let renderedContent: String = render(source)
// include source for better assertion errors
let expected: String = showTabs(expectedResult + "\n\n" + source)
let actual: String = showTabs(renderedContent + "\n\n" + source)
return expected.toString() == actual.toString()
}
private static func showTabs(s: String): String {
// Tabs are shown as "rightwards arrow" for easier comparison
return s.replace("\t", "\u{2192}")
}
}
public class TablesTest <: RenderingTestCase {
private static let EXTENSIONS: Array<Extension> = Array<Extension>([TablesExtension.create()])
private static let PARSER: Parser = Parser.builder().extensions(EXTENSIONS).build()
private static let RENDERER: HtmlRenderer = HtmlRenderer.builder().extensions(EXTENSIONS).build()
protected override func render(source: String): String {
return RENDERER.render(PARSER.parse(source))
}
}
3 Render
前置条件:NA
场景:
约束:NA
可靠性:NA
3.1 TextRender
3.1.1 主要接口
public class TextContentRenderer <: Renderer {
/*
* 构建TextContentRendererBuilder对象
* 返回值 TextContentRendererBuilder - TextContentRendererBuilder
*/
public static func builder(): TextContentRendererBuilder
/*
* 渲染node 追加到StringBuilder中
* 参数 Node - Ndoe
* 参数 StringBuilder - StringBuilder文本
*/
public override func render(node: Node, output: StringBuilder): Unit
/*
* 渲染node
* 参数 Node - Ndoe
* 返回值 String - 渲染完成的文本
*/
public override func render(node: Node): String
}
public class TextContentRendererBuilder {
/*
* 构建 TextContentRenderer 对象
* 返回值 TextContentRenderer - TextContentRenderer
*/
public func build(): TextContentRenderer
/*
* 是否忽略换行符 true是忽略
* 参数 Bool - 是否忽略换行符
* 返回值 TextContentRendererBuilder - TextContentRendererBuilder
*/
public func setStripNewlines(stripNewlines: Bool): TextContentRendererBuilder
/*
* 新增一个 TextContentNodeRendererFactory实例对象
* 参数 TextContentNodeRendererFactory - TextContentNodeRendererFactory
* 返回值 TextContentRendererBuilder - TextContentRendererBuilder
*/
public func nodeRendererFactory(nodeRendererFactory: TextContentNodeRendererFactory): TextContentRendererBuilder
/*
* 拓展新的render 例如 TablesExtension
* 参数 Iterable<Extension> - 拓展列表
* 返回值 TextContentRendererBuilder - TextContentRendererBuilder
*/
public func extensions(extensions: Iterable<Extension>): TextContentRendererBuilder
}
public interface TextContentRendererExtension <: Extension {
/*
* 拓展新的render 例如 TablesExtension
* 参数 TextContentRendererBuilder - TextContentRendererBuilder
*/
func ext(rendererBuilder: TextContentRendererBuilder): Unit
}
public interface TextContentNodeRendererContext {
/*
* 是否忽略换行符 true是忽略
* 返回值 Bool - 是否忽略换行符
*/
func stripNewlines(): Bool
/*
* 获取TextContentWriter
* 返回值 TextContentWriter - 是否忽TextContentWriter略换行符
*/
func getWriter(): TextContentWriter
/*
* 渲染render
* 参数 Node - Node
*/
func render(node: Node): Unit
}
public type TextContentNodeRendererFactory = (context: TextContentNodeRendererContext) -> NodeRenderer
public class TextContentWriter {
/*
* 构建TextContentWriter对象
* 参数 StringBuilder - 初始文本
*/
public TextContentWriter(out: StringBuilder)
/*
* 写入空格 " "
*/
public func whitespace(): Unit
/*
* 写入冒号 ":"
*/
public func colon(): Unit
/*
* 写入 "\n"
*/
public func line(): Unit
/*
* 去除文本的 [\r\n\s]格式
* 参数 ?String - 文本
*/
public func writeStripped(s: ?String): Unit
/*
* 写入文本
* 参数 ?String - 文本
*/
public func write(s: ?String): Unit
/*
* 写入文本
* 参数 Rune - 文本
*/
public func write(c: Rune): Unit
}
3.1.2 示例
import commonmark4cj.commonmark.*
@TestCase
func render_test():Unit {
var source: String = ""
var rendered: String = ""
source = "foo bar"
rendered = defaultRenderer().render(parse(source))
assertEquals("foo bar", rendered)
rendered = strippedRenderer().render(parse(source))
assertEquals("foo bar", rendered)
source = "foo foo\n\nbar\nbar"
rendered = defaultRenderer().render(parse(source))
assertEquals("foo foo\nbar\nbar", rendered)
rendered = strippedRenderer().render(parse(source))
assertEquals("foo foo bar bar", rendered)
}
func defaultRenderer(): TextContentRenderer {
return TextContentRenderer.builder().build()
}
func strippedRenderer(): TextContentRenderer {
return TextContentRenderer.builder().setStripNewlines(true).build()
}
3.2 HtmlRender
3.2.1 主要接口
public class HtmlRenderer <: Renderer {
/*
* 构建HtmlRendererBuilder对象
* 返回值 HtmlRendererBuilder - HtmlRendererBuilder
*/
public static func builder(): HtmlRendererBuilder
/*
* 渲染node 追加到StringBuilder中
* 参数 Node - Ndoe
* 参数 StringBuilder - StringBuilder文本
*/
public override func render(node: Node, output: StringBuilder): Unit
/*
* 渲染node
* 参数 Node - Ndoe
* 返回值 String - 渲染完成的文本
*/
public override func render(node: Node): String
}
public class HtmlRendererBuilder {
/*
* 构建 HtmlRenderer 对象
* 返回值 HtmlRenderer - HtmlRenderer
*/
public func build(): HtmlRenderer
/*
* 更改 softbreak 默认 "\n"
* 参数 String - softbreak
* 返回值 HtmlRendererBuilder - HtmlRendererBuilder
*/
public func softbreak(softbreak: String): HtmlRendererBuilder
/*
* 是否需要转义 默认 false
* 参数 Bool - 是否需要转义
* 返回值 HtmlRendererBuilder - HtmlRendererBuilder
*/
public func escapeHtml(escapeHtml: Bool): HtmlRendererBuilder
/*
* 是否URL编码 默认 false
* 参数 Bool - 是否URL编码
* 返回值 HtmlRendererBuilder - HtmlRendererBuilder
*/
public func percentEncodeUrls(percentEncodeUrls: Bool): HtmlRendererBuilder
/*
* 新增属性工厂类
* 参数 AttributeProviderFactory - AttributeProviderFactory
* 返回值 HtmlRendererBuilder - HtmlRendererBuilder
*/
public func attributeProviderFactory(attributeProviderFactory: AttributeProviderFactory): HtmlRendererBuilder
/*
* 新增属性渲染工厂类
* 参数 HtmlNodeRendererFactory - HtmlNodeRendererFactory
* 返回值 HtmlRendererBuilder - HtmlRendererBuilder
*/
public func nodeRendererFactory(nodeRendererFactory: HtmlNodeRendererFactory): HtmlRendererBuilder
/*
* 拓展新的render 例如 TablesExtension
* 参数 Iterable<Extension> - 拓展列表
* 返回值 HtmlRendererBuilder - HtmlRendererBuilder
*/
public func extensions(extensions: Iterable<Extension>): HtmlRendererBuilder
}
public interface HtmlRendererExtension <: Extension {
/*
* 拓展新的render 例如 TablesExtension
* 参数 HtmlRendererBuilder - HtmlRendererBuilder
*/
func ext(rendererBuilder: HtmlRendererBuilder): Unit
}
public class HtmlWriter {
/*
* 初始化
* 参数 StringBuilder - 初始文本
*/
public init(out: StringBuilder)
/*
* 新增文本
* 参数 String - 文本
*/
public func raw(s: String): Unit
/*
* 新增转义后的文本
* 参数 String - 文本
*/
public func text(text: String): Unit
/*
* 新增标签
* 参数 String - 文本
*/
public func tag(name: String): Unit
/*
* 新增标签
* 参数 String - 文本
* 参数 Map<String, String> - 属性map
*/
public func tag(name: String, attrs: Map<String, String>): Unit
/*
* 新增标签
* 参数 String - 文本
* 参数 Map<String, String> - 属性map
* 参数 Bool - 是否需要闭合 " /"
*/
public func tag(name: String, attrs: ?Map<String, String>, voidElement: Bool): Unit
/*
* 新增 "\n"
*/
public func line(): Unit
}
public interface AttributeProvider {
/*
* 设置标签属性
* 参数 Node - Node
* 参数 String - 标签
* 参数 Map<String, String> - 属性map
*/
func setAttributes(node: Node, tagName: String, attributes: Map<String, String>): Unit
}
public interface AttributeProviderContext {}
public type AttributeProviderFactory = (context: AttributeProviderContext) -> AttributeProvider
public interface HtmlNodeRendererContext {
/*
* URL编码
* 参数 String - url
* 返回值 String - 编码后的url
*/
func encodeUrl(url: String): String
/*
* 拓展自定义的tag属性
* 参数 Node - 被应用的Node
* 参数 String - 标签
* 参数 Map<String, String> - 属性map
* 返回值 Map<String, String> - 拓展后的属性map
*/
func extendAttributes(node: Node, tagName: String, attributes: Map<String, String>): Map<String, String>
/*
* 获取HtmlWriter
* 返回值 HtmlWriter - HtmlWriter
*/
func getWriter(): HtmlWriter
/*
* 获取HtmlWriter 默认 "\n"
* 返回值 HtmlWriter - HtmlWriter
*/
func getSoftbreak(): String
/*
* 渲染Node
* 参数 Node - Node
*/
func render(node: Node): Unit
/*
* 是否需要转义 默认false
* 返回值 Bool - Bool
*/
func shouldEscapeHtml(): Bool
}
public type HtmlNodeRendererFactory = (context: HtmlNodeRendererContext) -> NodeRenderer
3.2.2 示例
import commonmark4cj.commonmark.*
@TestCase
func render_test():Unit {
let rendered: String = htmlAllowingRenderer().render(
parse("paragraph with <span id='foo' class=\"bar\">inline & html</span>"))
assertEquals("<p>paragraph with <span id='foo' class=\"bar\">inline & html</span></p>\n", rendered)
}
private func htmlAllowingRenderer(): HtmlRenderer {
return HtmlRenderer.builder().escapeHtml(false).build()
}