内容提要,学习本文,你会学到:
markmap
这个库实现的核心技术原理Markmap
是一个非常有用的工具,它可以将 Markdown 文本转换成交互式的思维导图
,我在工作中经常会用到这个工具,比如:
因此之前就了解了下这个工具的实现原理,今天整理了下之前的笔记,写了出来。
学习本文,我们不仅仅可以了解markmap这个库实现的技术原理,今天更重要的其实是,学会一种思想,即Markdown 由于其结构化的特性,可以很容易地被解析成 AST(抽象语法树),这为转换成各种不同格式提供了可能,今天markdown可以转换为 思维导图,明天就可以转换为PPT,这里面透露这的道理,我想表达的意思是,其实他们是相通的。先剧透一下,如下图。
先不讲这么多了,先来了解一下,markmap 的核心技术原理,markmap是开源的,其github地址在此,https://github.com/markmap/markmap ,6k的点赞,然后其issue也不多,让我没法拒绝去扒开他的原理一探究竟。
它的实现原理基于几个关键的技术点:
marked
或其他库)来解析输入的 Markdown 文本,将其转换成一个抽象语法树(AST)。这个 AST 描述了 Markdown 文本的结构,包括标题、列表、代码块等。下面是这个过程的序列图
实际上,我们不难发现,树形结构转换就是这个库的重点即,怎么讲markdown结果的文本转换最终转换为可渲染成svg的语言的,带着这个重点,我们去看一看,他是如何实现的。
这个过程涉及到遍历 AST 并创建一个节点树,其中每个节点代表一个思维导图的节点。为了极度的简化,我整理出了一个简化的 JavaScript 伪代码,描述了这个转换过程的核心逻辑:
function transformASTToTree(ast) {
const rootNode = createNode('root');
let currentNode = rootNode;
for (const item of ast) {
switch (item.type) {
case 'heading':
// 根据标题级别确定节点的深度
const level = item.depth;
// 如果当前标题级别更深,就创建一个子节点
if (level > currentNode.level) {
const childNode = createNode(item.text, level);
currentNode.children.push(childNode);
currentNode = childNode;
} else {
// 如果标题级别不深或者相同,就回溯到正确的父级
while (currentNode.level >= level) {
currentNode = currentNode.parent;
}
const siblingNode = createNode(item.text, level);
currentNode.children.push(siblingNode);
currentNode = siblingNode;
}
break;
// 处理其他类型的节点...
}
}
return rootNode;
}
function createNode(text, level = 0) {
return {
text,
level,
children: [],
parent: null
};
}
其整个过程可以使用下面的序列图表示:
以上就是这个库的核心原理。
其实不难发现,还有一些库是将markdown转换为PPT,他们的思路都貌似出奇的一致,markdown其实是一种结构话的标记语言,那么,他就可以转化为 ast,然后通过ast转化为其他的语言,所以,这种思路很重要,那么markdown借助ast还可能萌发出什么新的玩法呢,我想大概可以有一下的玩法:
这里之给出一个markdown转代码的示例,比如我们有一个这样的markdown文件
# Class: Car
## Properties
- `string` color
- `int` year
## Methods
### Method: start
- Description: Start the car engine.
- Returns: `void`
### Method: getColor
- Description: Get the color of the car.
- Returns: `string`
我们就可以写一个工具
import re
# 假设的 Markdown 文本
markdown_text = """
# Class: Car
## Properties
- `string` color
- `int` year
## Methods
### Method: start
- Description: Start the car engine.
- Returns: `void`
### Method: getColor
- Description: Get the color of the car.
- Returns: `string`
"""
# 解析类名
class_match = re.search(r'# Class: (\\w+)', markdown_text)
class_name = class_match.group(1) if class_match else 'UnknownClass'
# 解析属性
properties = re.findall(r'- `(\\w+)` (\\w+)', markdown_text)
# 解析方法
methods = re.findall(r'### Method: (\\w+).*?- Returns: `(\\w+)`', markdown_text, re.DOTALL)
# 生成代码
code = f"class {class_name}:\\n"
code += " def __init__(self):\\n"
for prop_type, prop_name in properties:
code += f" self.{prop_name} = None # Expected type: {prop_type}\\n"
for method_name, return_type in methods:
return_statement = 'pass' if return_type == 'void' else f'return None # Expected type: {return_type}'
code += f"\\n def {method_name}(self):\\n"
code += f" {return_statement}\\n"
print(code)
下面是我们转换的结果,基本上就实现了将markdown转化为代码,虽然我们取巧,并没有用ast,但是思路上实际是一致,解析里面结构化的信息,来做转化。
将虽然,这种方式不见得比我们直接写代码来的快,但是毕竟这里打开了另外一种思路。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。