最近研究 Markdown 解析的技巧,如果解析 Markdown 文档,识别文本内容类型是一个关键步骤。markdown-it
是一款热门的、开源的、高效库的且易于可扩展的解析器开源库。今天深入该库部分源码,看看该库使用哪些技巧对Markdown中的code_block
(代码块)语法解析。
一、Markdown 代码块语法
markdown 标准语法中,想要创建代码块,可以将每一行缩进至少四个空格或者制表符,如
<html>
<head>
</head>
</html>
渲染效果如下
<html>
<head>
</head>
</html>
要创建不用缩进的代码块,可以使用围栏式代码块,就是使用三个反引号
```
二、 功能解析
markdown-it
仓库中对代码块解析源码如链接:
https://github.com/markdown-it/markdown-it/blob/master/lib/rules_block/code.mjs
// Code block (4 spaces padded)
export default function code (state, startLine, endLine/*, silent */) {
if (state.sCount[startLine] - state.blkIndent < 4) { return false }
let nextLine = startLine + 1
let last = nextLine
while (nextLine < endLine) {
if (state.isEmpty(nextLine)) {
nextLine++
continue
}
if (state.sCount[nextLine] - state.blkIndent >= 4) {
nextLine++
last = nextLine
continue
}
break
}
state.line = last
const token = state.push('code_block', 'code', 0)
token.content = state.getLines(startLine, last, 4 + state.blkIndent, false) + '\n'
token.map = [startLine, state.line]
return true
}
-
该方法的第一条语句是识别是否满足markdown代码块的条件
// markdown 代码行要求每行前面至少四个空格 if (state.sCount[startLine] - state.blkIndent < 4) { return false }
其中,
state.sCount[startLine]
和state.blkIndent
分别代码当前行的空格数和块级缩进基准值。只有当实际缩进大于等于四个空格时候,才视为代码块的起始。如果不是代码块,则返回false,留给下一个规则解析,否则解析完成返回true,意味着该规则函数成功处理特定的 markdonw 结构。 -
确定代码块的范围
下面这个循环是检查后续行,并确定代码块的开始、结束行数
while (nextLine < endLine) { if (state.isEmpty(nextLine)) { nextLine++ continue } if (state.sCount[nextLine] - state.blkIndent >= 4) { nextLine++ last = nextLine continue } break }
- 空行处理:在这个循环中,函数逐行检查,如果是空行就跳转到下一行,
- 缩进检查:如果行的缩进大于四个空格,则视为代码块的一部分,更新 last 变量,记录代码结束的位置
- 结束循环:如果既不是空行且行缩进小于四空格,则认为代码块结束,退出循环
-
生成代码Token
确定了代码块的访问,函数即可以创建新的 token,复制 content,并将记录解析的行位置
const token = state.push('code_block', 'code', 0) token.content = state.getLines(startLine, last, 4 + state.blkIndent, false) + '\n' token.map = [startLine, state.line]
其中
state.getLines
方法提取从起始行位置,到结束行位置的内容,并移除四个空格。
三、总结
- 逐行扫描:通过逐行检查输入文本,确保对代码精准识别,其中起始、结束条件判断灵活,不管是规则修改(比如改成五个空格),还是数据源修改(比如改成文件流)都易于扩展。这种思想在许多领域都有应用,比如JPEG 编解码库
- Token的生成:在识别出代码后,立即生成响应的 token,这种设计使得解析与渲染的过程解耦。