Example Obsidian Links
【免费下载链接】harperOffline, privacy-first grammar checker. Fast, open-source, Rust-powered项目地址: https://gitcode.com/GitHub_Trending/har/harper
Below, you will find a number of example links that Obsidian is able to process.
These should be treated as normal Markdown links. The things inside the square brackets are visible and should be checked by Harper.
[[Three lws of motion]] Three las of motion
Wikilinks allow you to replace the visible text with a pipe (|) operator. The text to the left of the operator should be ignored.
[[lnk tget|Link Text]]
这份文件同时承担了三个角色,逐层拆解如下: 1. **它本身是一篇被检查的文档**。文件中故意埋入了拼写错误:`lws`(应为 *laws*)、`las`(应为 *laws*)、`lnk tget`(应为 *link target*),用来验证哪些位置的错误会被 Harper 捕获。 2. **它定义了 Wiki 链接的语义契约**,原文档中给出了两条明确规则: - “These should be treated as normal Markdown links. The things inside the square brackets are visible and should be checked by Harper.”——双括号里的文字**对 Harper 可见**,应当参与拼写与语法检查; - “The text to the left of the operator should be ignored.”——当使用竖线 `|` 运算符时(`[[target|display]]`),竖线**左侧**的目标笔记名应当被**忽略**,只有右侧的显示文本参与检查。 3. **它作为回归测试的输入**,与 [run_tests.rs](https://link.gitcode.com/i/457147c577a081dc2c28cf4b99dbe250) 中的宏绑定,断言 lint 总数。 值得注意的设计是:同一个目标笔记“Three laws of motion”分别以 Wiki 链接 `[[Three lws of motion]]` 和标准 Markdown 链接 `Three las of motion` 两种形式出现。这直接验证了原文档中“treated as normal Markdown links”的契约——两种链接形式必须得到相同的检查待遇,任何一侧的差异都会改变预期 lint 数量并让测试失败。 ## 测试断言:预期恰好 2 个 lint 在 [run_tests.rs](https://link.gitcode.com/i/457147c577a081dc2c28cf4b99dbe250#L74) 中,该文件通过 `create_test!` 宏注册: ```rust create_test!(obsidian_links.md, 2, Dialect::American);这个断言精确编码了上述语义契约。create_test!宏(定义于 run_tests.rs)的工作原理是:
- 用
include_str!将./test_sources/obsidian_links.md编译期嵌入二进制; - 调用
Document::new_markdown_default(&source, &dict)以默认 Markdown 解析器 和FstDictionary::curated()受审词典构建文档; - 用
LintGroup::new_curated(dict, Dialect::American)执行美式英语全套 linter,断言lints.len() == 2; - 额外校验每个 token 的 span 都指向真实存在的字符(
token.span.try_get_content(...).is_some()),防止解析器产出越界的 token 区间。
预期 2 个 lint 的来源是:[[Three lws of motion]]中的lws(拼写错误)和[Three las of motion]链接文本中的las(拼写错误)——两处都是可见文本。而[[lnk tget|Link Text]]中竖线左侧的lnk tget虽然也是拼写错误,但按规则应被解析器剥离,因此不产生 lint;右侧的Link Text拼写正确,同样不产生 lint。如果解析器误把隐藏目标也拿去检查,lint 数会变成 4,测试即失败。Document::new_markdown_default的入口实现在 document.rs,它只是MarkdownOptions::default()配置的薄封装,即 Wiki 链接处理无需任何额外开关,默认行为即生效。
解析器实现:两个 Wiki 链接处理函数
上述语义契约在 harper-core/src/parsers/markdown.rs 中由两个函数落地。在Parser::parse主流程的末尾(markdown.rs),解析完成、标点/HTML 等事件处理结束后,会依序执行:
Self::remove_hidden_wikilink_tokens(&mut tokens); Self::remove_wikilink_brackets(&mut tokens);1. remove_hidden_wikilink_tokens:剥离竖线左侧的隐藏目标
该函数(markdown.rs)处理形如[[Target text|Display Text]]的链接,函数文档注释也直接引用了这个语法示例:
[[Target text|Display Text]]其算法分三步:
- 定位所有竖线:遍历
tokens.iter_pipe_indices()找出每个|token 的下标; - 向前回溯找
[[:从竖线前第 2 个 token 开始逐个回退,直到找到相邻的两个开方括号,或遇到换行、越界(cursor == 0)为止; - 向后前瞻找
]]:从竖线后 1 位开始逐个前进,找到相邻的两个闭方括号,或遇到换行为止。
只有当[[和]]都在同一行内找到时,才执行删除:移除open_bracket_idx..=pipe_idx区间(即[[、隐藏目标的所有 token 和竖线本身),再额外移除close_bracket_idx与close_bracket_idx + 1(即]]的两个括号)——注意它保留了]]与竖线之间的显示文本(Display Text)以及[[到竖线之前区间之外的内容。批量删除通过 vec_ext.rs 中定义的remove_indices(&mut VecDeque<usize>)一次完成,保证 span 位置的一致性。
对应到测试文件,[[lnk tget|Link Text]]经此函数处理后,lnk、tget及其前后括号均被剔除,剩余可见 token 为Link、Text。
2. remove_wikilink_brackets:去除无竖线链接的括号
对不含竖线的普通 Wiki 链接(如[[Three lws of motion]]),由 remove_wikilink_brackets 处理。它用单游标线性扫描:遇到相邻的[[时记录起点下标;之后若在同一行内遇到相邻的]],就把开括号的两个 token 和闭括号的两个 token 都加入删除队列,并将起点复位。关键在于只删括号、不删内容——链接内部的Three、lws、of、motion等 token 原样保留,于是lws依然会被拼写检查器捕获,这正是测试中 2 个 lint 的其中之一。
两个函数都以换行作为扫描边界:Wiki 链接跨行时不会配对成功,括号会被当作普通标点保留。这与 Obsidian 的 Wiki 链接不可跨行的实际语义一致。
边界情况:解析器单元测试覆盖的极端输入
markdown.rs 的tests模块针对 Wiki 链接专门编写了若干极端输入测试,它们与 obsidian_links.md 的文档级测试形成互补:
| 测试用例 | 输入 | 断言结果 |
|---|---|---|
normal_wikilink | [[Wikilink]] | 只剩一个TokenKind::Word(_),四个括号全部被剥离 |
hidden_wikilink_text | [[this is hidden\|this is not]] | 只剩 3 个Word+ 2 个Space(this is not),左侧 3 词连同括号、竖线全部消失 |
empty_wikilink_text | [[\|]] | token 列表为空——目标与显示文本均缺失时整段剥离 |
improper_wikilink_text | this is shown\|this is also shown]] | 无[[开头时不触发剥离:所有 token(含\|与]])原样保留为词、空格、Punctuation::Pipe与Punctuation::CloseSquare |
hang | [[#\|]]:A] | 只要求不挂死(历史回归问题),不做断言 |
其中improper_wikilink_text特别值得注意:它证实剥离逻辑依赖完整的双开括号,孤立的竖线加孤立的]]会被当作普通标点处理,不会误删正文内容。hang用例则记录了[[#|]]:A]这类畸形输入曾经导致解析器卡住的问题,如今仅作为不崩溃的冒烟检查。
解析流程定位:Wiki 链接处理在管线中的位置
从源码结构看,整条 Markdown 解析管线为:
Markdown::parse(markdown.rs)先用pulldown_cmark以Options::all()减去ENABLE_SMART_PUNCTUATION的方式做 CommonMark 词法分析,并借助build_byte_to_char_map把字节偏移换算为 Harper 内部的字符偏移;- 事件循环中,代码块、行内数学、HTML 等被整体标记为
TokenKind::Unlintable;普通Text事件则委托PlainEnglish解析器切分为Word/Space/Punctuation等细粒度 token;链接文本默认参与检查(可通过MarkdownOptions.ignore_link_title关闭); - 事件循环结束后,依次运行
remove_hidden_wikilink_tokens与remove_wikilink_brackets完成 Wiki 链接的“去壳”处理; - 最后
Document的构造阶段还会执行apply_fixups(见 document.rs):合并多余空格与换行、压缩缩写点、配对引号等,为 linter 准备规整的 token 序列。
也就是说,Wiki 链接的处理发生在词法分析的最后阶段,且以“物理删除 token”而非“标记为不可检查”的方式实现——被隐藏的文本根本不会进入 linter 的视野,因此既不会产生误报,也不会被任何 lint 规则(包括拼写检查)触碰。
复现与验证:如何运行这一测试
该测试是harper-core集成测试套件的一部分,在仓库根目录可用 Cargo 定向运行:
cargo test -p harper-core --test run_tests lints_obsidian_links_md_correctly生成的测试函数名为lints_obsidian_links_md_correctly(由宏内paste::paste!按lints_ + 文件名 + _correctly拼接而来)。运行时会通过dbg!打印实际检出的 lint 列表,与预期的 2 个拼写类 lint 对照即可确认 Wiki 链接语义是否符合 obsidian_links.md 开头声明的规则。解析器层面的细粒度行为则可单独验证:
cargo test -p harper-core --lib parsers::markdown::tests【免费下载链接】harperOffline, privacy-first grammar checker. Fast, open-source, Rust-powered项目地址: https://gitcode.com/GitHub_Trending/har/harper
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考