引用与反向查找实现
本文记录笔记库内 Markdown 文件引用的生成、插入和反向查找实现。用户可见的操作规则以 功能说明 和 引用查找 为准。
引用生成
入口与目标
- 标签、文件树右键菜单和命令面板调用
src/renderer/runtime/outline-path-copy.mts。 - 仅已保存、位于当前笔记库内的 Markdown 文件可作为引用目标。支持
.md、.markdown、.mdown和.mkd。 - “复制引用”由
workspaceDocumentMarkdownLink生成文件顶部链接;“复制引用(大纲)”由workspaceOutlineMarkdownLink生成标题链接。这两个函数位于src/renderer/workspace/path-copy.mts。 - 快速打开中的
Ctrl/Cmd+Enter插入引用复用同一组链接构造函数,入口在src/renderer/runtime/quick-open-actions.mts。标题结果先从已打开编辑器或磁盘取得正文,再根据匹配的标题行计算片段。
文件路径和标题锚点
- 链接目的地以
/开头,表示笔记库根相对路径。路径逐段做 URI 编码,链接文字转义反斜杠和方括号。 - 大纲引用根据目标文档保留的 caret 位置,选择该位置或之前最近的 Markdown 标题。反向选择使用活动选择端。
headingAtCaret使用extractMarkdownOutline提取标题,再用markdownHeadingFragments生成片段;重复标题得到-1、-2等唯一后缀。中文等字母和数字在片段中保持可读,特殊字符编码。- 文件未打开、caret 位于首个标题之前或没有标题时,大纲复制退化为文件链接。复制只写剪贴板,不改变编辑器位置或导航历史。
- 生成结果示例:
[目标.md](/目录/目标.md)、[章节](/目录/目标.md#章节)。
反向查找调用链
src/renderer/runtime/references.mts的findContextReferences从标签、文件树或当前文档确定目标。findReferencesForPath对“当前标题”模式调用headingAtCaret;没有可用标题时查询整个文件。- 渲染进程创建或复用一个
reportType: 'references'的只读报告标签。referenceBuffers读取当前窗口所有已打开、已命名 Markdown 页面的实时正文,不触发保存;未打开文件由主进程从磁盘读取。 refreshReferenceReport经预加载层的findWorkspaceReferences调用workspace:find-referencesIPC。主进程的src/main/workspace-handlers.ts传入当前笔记库根路径及文件清单,src/main/references.ts执行扫描。src/renderer/search/references.mts把结果转换成只读文本、逐行导航目标和目的地高亮范围。报告按来源文件分组,列出每处链接的行号、目的地和源码行。
扫描与匹配
- 主进程先校验请求、Markdown 扩展名、目标是否属于文件清单,以及缓冲区快照的路径和类型。请求最多携带 10,000 个缓冲区;标题片段最长 10,000 字符。
- 目标和每个来源都经
realpath检查,拒绝越出笔记库的路径和符号链接。即使来源正文来自渲染进程快照,也必须通过真实路径检查。 - 扫描范围是当前笔记库文件清单中的 Markdown 文件,按清单顺序逐个读取。已打开文件用请求时的正文快照覆盖磁盘内容。
- 链接解析复用
src/main/broken-links.ts的parseMarkdownLinks和decodeDestination:识别 Markdown 行内链接、引用式链接、HTMLhref;代码围栏及行内代码不会产生链接。图片、外部地址、无效 URI 编码不计入结果。 - 相对路径、笔记库根路径及空路径的同文件锚点由
resolveWorkspaceMarkdownLink解析。最终比较真实文件路径;Windows 上路径比较忽略大小写。 - 文件查询统计指向该文件的全部链接,包括带标题片段的链接。标题查询先在目标当前正文中确定标题行,再把每处链接的片段解析为标题行;只有同一标题行才匹配,因此重复标题的唯一锚点可以区分。
- 同一源码行的多个链接各计一次。最多收集 10,000 处;超限设置
truncated。单个来源读取失败会记录在failures并继续扫描;目标不存在或指定标题已消失则整个请求报错。
报告生命周期与导航
- 报告是一次查询快照。点击“刷新引用”重新收集打开文档正文并扫描;普通输入不会自动刷新。
- 每次刷新递增报告的请求令牌。异步响应仅在笔记库未切换、报告仍存在且令牌仍为当前值时写入,避免旧结果覆盖新查询。
- 结果行与来源文件、行号关联。双击打开来源并进入现有前进/后退历史;
F4和Shift+F4在各处引用间循环。导航仍指向来源行,不选中链接文字。 - 报告不保存为普通笔记,也不写回来源文件。引用跳转的历史机制另见 编辑器导航历史。
验证入口
node scripts/run.mts node --test test/references.test.js覆盖路径解析、标题锚点、重复引用、未保存快照、无效请求、读取失败、结果上限及符号链接逃逸。npm run test:smoke:references覆盖界面查找、报告导航、刷新和失效链接报告流程。
