Markdown语法支持:Harper如何处理格式化文本
【免费下载链接】harper The Grammar Checker for Developers 项目地址: https://gitcode.com/gh_mirrors/har/harper
开发者在编写技术文档、README或注释时,经常使用Markdown(标记语言)来添加标题、列表、代码块等格式化元素。Harper作为面向开发者的语法检查工具,需要智能区分文本内容与格式标记,确保只对实际内容进行语法分析。本文将解析Harper的Markdown解析机制,展示其如何处理复杂格式化文本。
Markdown解析器核心实现
Harper的Markdown支持由harper-core/src/parsers/markdown.rs模块实现,该模块定义了Markdown结构体,继承自基础PlainEnglish解析器,专门处理CommonMark格式文件。其核心功能是过滤格式标记,提取可检查的文本内容,同时忽略代码块、表格等无需语法检查的元素。
解析器通过parse方法实现文本处理,主要流程包括:
- 使用
pulldown_cmark库解析Markdown事件流 - 跟踪文本位置偏移量,区分格式标记与实际内容
- 对不同Markdown元素应用差异化处理策略
- 生成过滤后的令牌流供后续语法检查
关键处理策略
代码块与数学公式过滤
技术文档常包含代码块和数学公式,这些内容不应被语法检查。Markdown解析器会将以下元素标记为Unlintable类型:
match event {
pulldown_cmark::Event::InlineMath(code)
| pulldown_cmark::Event::DisplayMath(code)
| pulldown_cmark::Event::Code(code) => {
tokens.push(Token {
span: Span::new_with_len(traversed_chars, chunk_len),
kind: TokenKind::Unlintable,
});
}
// 处理HTML内容
pulldown_cmark::Event::Html(_content)
| pulldown_cmark::Event::InlineHtml(_content) => {
tokens.push(Token {
span: Span::new_with_len(traversed_chars, size),
kind: TokenKind::Unlintable,
});
}
}
列表与段落结构处理
解析器会为列表项、段落和标题添加适当的段落分隔符,确保语法检查时的句子边界识别准确:
pulldown_cmark::Event::End(pulldown_cmark::TagEnd::Paragraph)
| pulldown_cmark::Event::End(pulldown_cmark::TagEnd::Item)
| pulldown_cmark::Event::End(pulldown_cmark::TagEnd::Heading(_)) => {
tokens.push(Token {
span: Span::new_with_len(tokens.last().map_or(0, |last| last.span.end), 0),
kind: TokenKind::ParagraphBreak,
});
stack.pop();
}
Wikilink特殊处理
针对Obsidian等工具常用的Wikilink语法[[链接文本|显示文本]],解析器提供专门处理:
remove_hidden_wikilink_tokens方法移除管道符左侧的隐藏文本remove_wikilink_brackets方法剥离双括号标记,保留实际链接文本
// 移除Wikilink中的隐藏文本
fn remove_hidden_wikilink_tokens(tokens: &mut Vec<Token>) {
let mut to_remove = VecDeque::new();
for pipe_idx in tokens.iter_pipe_indices() {
// 定位前置`[[`和后置`]]`
// 标记需移除的隐藏文本范围
to_remove.extend(open_bracket_idx..=pipe_idx);
to_remove.push_back(close_bracket_idx);
to_remove.push_back(close_bracket_idx + 1);
}
tokens.remove_indices(to_remove);
}
配置选项与灵活性
Markdown解析器提供可配置选项,通过MarkdownOptions结构体控制解析行为:
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
#[non_exhaustive]
pub struct MarkdownOptions {
pub ignore_link_title: bool, // 是否忽略链接标题
}
当ignore_link_title设为true时,链接文本将被标记为不可检查,避免对URL或文件名进行语法检查:
if matches!(tag, Tag::Link { .. }) && self.options.ignore_link_title {
tokens.push(Token {
span: Span::new_with_len(traversed_chars, text.chars().count()),
kind: TokenKind::Unlintable,
});
continue;
}
测试用例覆盖
解析器配套完整测试用例,验证各种Markdown场景的处理正确性:
| 测试方法 | 功能描述 |
|---|---|
survives_emojis | 验证表情符号处理不会导致崩溃 |
math_becomes_unlintable | 确保数学公式被正确标记为不可检查 |
hidden_wikilink_text | 测试Wikilink隐藏文本移除功能 |
link_title_unlintable | 验证链接标题可配置忽略功能 |
issue_880 | 确保代码块后正确添加段落分隔符 |
例如,normal_wikilink测试验证普通Wikilink处理:
#[test]
fn normal_wikilink() {
let source = r"[[Wikilink]]";
let tokens = Markdown::default().parse_str(source);
let token_kinds = tokens.iter().map(|t| t.kind.clone()).collect::<Vec<_>>();
assert!(matches!(token_kinds.as_slice(), &[TokenKind::Word(_)]))
}
实际应用场景
技术文档检查流程
- 开发者编写包含代码块、表格和公式的Markdown文档
- Harper调用Markdown解析器处理文档
- 解析器过滤格式标记,提取纯文本内容
- 语法检查器对过滤后的文本进行分析
- 最终结果保留原始格式,仅标记实际文本中的语法问题
配置示例
通过调整解析选项,可适应不同场景需求:
// 创建忽略链接标题的解析器
let parser = Markdown::new(MarkdownOptions {
ignore_link_title: true,
..MarkdownOptions::default()
});
// 解析包含链接的文本
let source = r"[harper](https://link.gitcode.com/i/aeae515d85508f93e3ccf275842ef583)";
let tokens = parser.parse_str(source); // 链接文本将被标记为Unlintable
Harper的Markdown解析器通过精准识别格式元素与内容边界,实现了对技术文档的智能语法检查。其模块化设计确保可扩展性,未来可支持更多Markdown扩展语法。开发团队可通过harper-core/src/parsers/markdown.rs查看完整实现,或通过测试用例了解具体场景处理方式。
【免费下载链接】harper The Grammar Checker for Developers 项目地址: https://gitcode.com/gh_mirrors/har/harper
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



