English 简体中文 繁體中文 한국 사람 日本語 Deutsch русский بالعربية TÜRKÇE português คนไทย french
查看: 9|回复: 0

markdown文本编辑器--核心功能(解析和渲染)

[复制链接]
查看: 9|回复: 0

markdown文本编辑器--核心功能(解析和渲染)

[复制链接]
查看: 9|回复: 0

264

主题

0

回帖

802

积分

高级会员

积分
802
ypd7izXMjAvL

264

主题

0

回帖

802

积分

高级会员

积分
802
2025-4-21 09:58:39 | 显示全部楼层 |阅读模式
🙌开源项目地址
🌍 GitHub 开源地址(YtyMark-java)
欢迎提交 PR、Issue、Star ⭐️!
1. 简述

YtyMark-java项目分为两大模块:

  • UI界面(ytyedit-mark)
  • markdown文本解析和渲染(ytymark)
本文主要内容为核心模块--markdown文本解析和渲染
关于markdown文本解析器怎么设计,渲染器怎么实现,怎么解耦解析和渲染。在这整个流程中,如果通过设计模式实现高内聚低耦合,可重用,易于阅读,易于扩展,易于维护等。
该模块的主要目录结构:
YtyMark-java├── ytymark/│ ├── src/│ │ ├── main/│ │ │ ├── java/│ │ │ │ ├── annotation/ # 自定义注解│ │ │ │ ├── enums/ # 枚举值│ │ │ │ ├── node/ # 树节点(块级和行级节点)│ │ │ │ ├── parser/ # 解析器(块级和行级元素)│ │ │ │ ├── renderer/ # 渲染器(块级和行级元素)│ │ │ └── resources/│ ├── README.md│ └──pom.xml2.解析器

目标:将 Markdown 文本解析为节点树。
使用到的设计模式

  • 构建者模式:创建复杂解析器渲染器
  • 状态模式:对markdown文本不同语法做一些前置处理,裁剪成块级元素。
  • 责任链模式:按优先级匹配不同,处理复杂的块级元素解析及嵌套解析。
  • 策略模式:动态选择解析器完成行内元素的解析
  • 组合模式:表示 Markdown 语法结构(如段落、标题、列表)之间的树形结构。
  • 迭代器模式:通过迭代器结合递归来遍历节点树,遍历块级元素进行行内元素解析。
根据使用顺序逐一讲述。
2.1. 构建者模式

通过构建者模式来创建复杂的解析器渲染器,包括自定义解析器的加入。
最简单的解析器(默认支持的语法解析器)和HTML渲染器
// 构建解析器Parser parser = ParserBuilder.builder().build();// 构建渲染器Renderer renderer = RendererBuilder.builder().build(HtmlRenderer.class);加入自定义块级元素解析器或者行级元素的解析器:
Parser parser = ParserBuilder.builder()                .addDelimiter("_")                .addBlockParser(new ParagraphParserHandler())                .addInlineParser("_", new ItalicParser())                .build();除此之外,程序会在启动时,扫描org.ytymark.parser包中带有注解BlockParserHandlerType的类,所以还可以通过注解加入新的块级元素解析器。
比如表格解析器:只需要在块解析器类上面加入这个注解和对应的枚举类即可
// 枚举类public enum BlockParserHandlerEnum {    TABLE("TABLE",6),// 通过注解加入块级元素解析器@BlockParserHandlerType(type = BlockParserHandlerEnum.TABLE)public class TableParserHandler extends AbstractBlockParser implements ParserHandler {2.2. 状态模式

对markdown文本不同语法做一些前置处理,裁剪成块级元素。
在正式进行块级元素解析前,对原始markdown文本进行分割,分割成一块一块的。
由于markdown语法很多,不进行一些设计,那将是一坨难以阅读理解、难以维护和扩展的代码。
通过状态模式实现类似状态机的机制,当状态(语法)匹配时,自动流转到专门处理这个语法的程序,处理完之后分割成一个“块”(这个块就是一个块元素),再回到默认状态,然后继续处理后续的文本。具体代码位于:org.ytymark.parser.block.state包。

2.3. 责任链模式

按优先级匹配不同,处理复杂的块级元素解析及嵌套解析。
在正式进行块级元素解析前,状态模式将元素文本处理成块数据集合,这就像流水线上简单的打了包,但并不区分包裹里面是什么内容。接着将这些包裹丢上流水线(责任链)上,责任链根据程序初始化时定好的顺序,逐一检测包裹里的内容是什么,匹配得上的就直接丢给机器处理(解析),最终给包裹打上标签(包装成节点对象)。对应包裹里还有包裹的,便继续丢回流水线上进行打标签。
整个处理流程,如图:

块解析的代码
    public void parser(String text, Node node) {        List<String> blocks = this.splitBlock(text);        // 逐块处理文本        for (String block : blocks) {            blockParserChain.parser(block, node);        }    }2.4. 策略模式

动态选择解析器完成行级元素的解析
块级元素解析完成后,会形成块节点的节点树,再进行行级元素解析
public Node parse(String markdownText) {        Node root = new DocumentNode();        // 统一换行符,替换所有 \r\n 或 \r 为 \n        String normalizedText = markdownText.replaceAll("\r\n|\r", "\n");        // 块级元素解析        blockParserContext.parser(normalizedText, root);        // 行级元素解析        this.parseInlines(root);        return root;    }行级元素并不是所有块元素都需要进行处理,目前只对标题和段落块节点进行解析,因为其它块级元素的内容最终会通过段落节点进行保存。
根据语法特点,动态选择解析器完成行级元素的解析,关键代码如下:
// 检查字符对或单个字符,选择对应的解析器String possibleDelimiter = this.getPossibleDelimiter(line, i);InlineParser inlineParser = inlineParserMap.get(possibleDelimiter);if (inlineParser != null) {    // 找到合适地解析器,尝试解析    InlineNode inlineNode = inlineParser.parser(sourceLine, this);    if(inlineNode!=null){        node.addChildNode(inlineNode);    }}2.5. 组合模式和迭代器模式

表示 Markdown 语法结构(如段落、标题、列表)之间的树形结构,每个语法对应一个Node节点,在块级元素和行级元素的解析过程,最终组合成节点树。节点和迭代器源码位于org.ytymark.node包。
通过迭代器结合递归来遍历节点树,在解析阶段,用于遍历块级元素进行行内元素解析。
使用迭代器完成兄弟节点的遍历(广度遍历),再结合递归完成子节点遍历(深度遍历),具体源码如下:
/** * 行级元素解析 * @param parent 父节点 */@Overridepublic void parseInlines(Node parent) {    Iterator<Node> iterator = parent.createIterator();    while (iterator.hasNext()) {        // 获取下一个兄弟节点        Node node = iterator.next();        // 解析子节点行        if(node instanceof ParagraphNode){            inlineParserContext.parser(((ParagraphNode) node).getText(),node);        }        if(node instanceof HeadingNode) {            inlineParserContext.parser(((HeadingNode) node).getText(), node);        }        if(node.getFirstChild()!=null)            parseInlines(node);    }}3. 渲染器

目标:将 AST 语法树渲染为 HTML 文本预览。
使用到的设计模式

  • 中介者模式思想:加入AST节点树解耦解析器和渲染器,使其灵活地渲染成不同的文档。
  • 迭代器模式:通过迭代器结合递归来遍历节点树,比如遍历节点树完成渲染操作。
  • 访问者模式:负责分离节点数据与渲染操作,提高渲染的扩展性。
3.1. 中介者模式思想

在解析和渲染中间加入AST节点树,解耦解析器和渲染器,使得一次解析可以灵活地渲染成不同的文档。为了将低耦合,常常会在两者间多加一层。

3.2. 迭代器模式

通过迭代器结合递归来遍历节点树,在渲染阶段,遍历节点树完成渲染操作。
/** * 循环渲染兄弟节点 *    在实现这个抽象类的渲染器中,如果存在子节点行为就需要调用这个方法实现递归遍历子节点 */protected void renderChildren(Node parent) {    Iterator<Node> iterator = parent.createIterator();    while (iterator.hasNext()) {        // 获取下一个兄弟节点        Node next = iterator.next();        // 渲染节点        next.render(this);    }}3.3. 访问者模式

负责分离Node节点数据与渲染操作行为,提高渲染的扩展性。在每个节点类中,实现渲染逻辑时只需要编写以下代码:
@Overridepublic void render(Renderer renderer) {    renderer.render(this);}将渲染逻辑抽离出来,由渲染器接口实现类来实现具体的渲染逻辑,不同的实现类对应不同的渲染行为,目前只实现了HTML的渲染。
在构建器中选择渲染器类型:
Renderer renderer = RendererBuilder.builder().build(HtmlRenderer.class);解决渲染的扩展性(多样性)问题
如果需要将markdown文本渲染成普通文本,则只需要继承AbstractRenderer 抽象类,实现Renderer接口中所有方法即可。并且实现渲染逻辑非常简单,只需要关注当前节点要做的事情即可。
比如,表格最外层的渲染源码
@Overridepublic void render(TableNode tableNode) {    sbHTML.append("<table>\n");    renderChildren(tableNode);    sbHTML.append("</table>\n");}而兄弟节点(广度遍历)和嵌套子节点(深度遍历),只需要调用抽象类AbstractRenderer的 renderChildren(Node parent)方法即可完成渲染,使得渲染逻辑只需要关注当前节点的行为即可。比如上面的表格渲染代码renderChildren(tableNode);。

🚀4. 项目亮点


  • 💡 高度模块化,任何 Markdown 语法都能独立添加/修改。
  • 🧠 设计模式实战,适合做设计模式学习的项目。
  • 🖥️ 可按需获取,用户界面和文本解析渲染分为两个模块

    • 只使用用户界面源码,然后轻松切换成熟的解析器依赖,开发一个完整的markdown文本编辑器;
    • 仅学习文本解析渲染模块源码,不用关注用户界面源码。

  • 🧪 解析性能毫秒级,确保解析效率。
  • 🎯 轻松上手,使用JDK8 自带JavaFX模块,无需做额外处理。
  • 📦 开源项目,文档完善,方便学习和贡献。
✏️5. 总结

markdown 文本解析和渲染将多种设计模式融入到实际应用中,是一次系统性的 设计模式实践架构设计实践
更多详细内容可以前往笔者微信公众号回复:设计模式,来获取,后续有关设计模式的新资料都可以从这个入口获取到。

  • 秘籍1设计模式手册:《掌握设计模式:23种经典模式实践、选择、价值与思想》
  • 秘籍2练手项目:设计模式实战项目--markdown文本编辑器软件开发(已开源

查看往期设计模式文章的:设计模式
超实用的SpringAOP实战之日志记录
2023年下半年软考考试重磅消息
通过软考后却领取不到实体证书?
计算机算法设计与分析(第5版)
Java全栈学习路线、学习资源和面试题一条龙
软考证书=职称证书?
软考中级--软件设计师毫无保留的备考分享
三连支持!!!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

264

主题

0

回帖

802

积分

高级会员

积分
802

QQ|智能设备 | 粤ICP备2024353841号-1

GMT+8, 2025-5-1 23:08 , Processed in 2.771233 second(s), 23 queries .

Powered by 智能设备

©2025