Markdown语法支持:Harper如何处理格式化文本

Markdown语法支持:Harper如何处理格式化文本

【免费下载链接】harper The Grammar Checker for Developers 【免费下载链接】harper 项目地址: 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方法实现文本处理,主要流程包括:

  1. 使用pulldown_cmark库解析Markdown事件流
  2. 跟踪文本位置偏移量,区分格式标记与实际内容
  3. 对不同Markdown元素应用差异化处理策略
  4. 生成过滤后的令牌流供后续语法检查

关键处理策略

代码块与数学公式过滤

技术文档常包含代码块和数学公式,这些内容不应被语法检查。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(_)]))
}

实际应用场景

技术文档检查流程

  1. 开发者编写包含代码块、表格和公式的Markdown文档
  2. Harper调用Markdown解析器处理文档
  3. 解析器过滤格式标记,提取纯文本内容
  4. 语法检查器对过滤后的文本进行分析
  5. 最终结果保留原始格式,仅标记实际文本中的语法问题

配置示例

通过调整解析选项,可适应不同场景需求:

// 创建忽略链接标题的解析器
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 【免费下载链接】harper 项目地址: https://gitcode.com/gh_mirrors/har/harper

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值