D0001: Rua Markdown 模块系统与语义标注设计 (Historical)
设计目标
为个人知识库(PKM)在 Markdown 之上建立一套逻辑表示层,使其具备类似编程语言模块系统的层级组织、命名引用和元信息标注能力,同时保持 Markdown 的人类可读性与生态兼容性。
核心原则:
Markdown 为主:笔记的日常 authoring 仍然是
.md,不替换为 Typst 或其他格式。逻辑路径优先:引用基于模块路径
vault::pages::intro,而非文件系统路径或标题字符串。轻量标注:通过 Rust-like 的
#[...]{}attribute 语法为内容块附加机器可解析的元信息。LSP 原生:模块解析、跳转、补全、诊断由 Rua 核心 / LSP 提供,不依赖特定编辑器。
核心概念
概念 | 说明 | 示例 |
|---|---|---|
Vault | 整个知识库,逻辑根模块 |
|
Module | 一个 |
|
Scope | 用户显式标注的内容范围,可 inline 可 block,可跨多个 Block |
|
Block | 按 Markdown 结构隐式切分的索引单元,如段落、章节、代码块 | 一个段落 / 章节 |
Label | Scope 的标识符,唯一且互斥,可用于引用 |
|
Tag | Scope 的分类标记,可多个 |
|
Class | 展示/样式类,可多个 |
|
Attribute | 键值对元信息 |
|
模块系统
文件系统 ↔ 逻辑模块映射
规则:
目录结构决定 Module 的层级关系。
普通
.md文件的模块路径为其所在目录路径加上文件名(不含.md后缀)。README.md是特殊的:它的模块路径是其父目录的路径,而非README本身的名字。因此,
xxx.md与xxx/README.md不能共存于同一父目录下,二者会映射到同一个模块路径(与 Rust 中mod.rs规则一致)。若某目录下的
README.md不存在,该目录仍被视为一个空 Module 入口。
文件系统路径 | 逻辑路径 | 说明 |
|---|---|---|
|
| 根模块 |
|
| README 继承父目录名 |
|
| 普通文件,模块名为 |
|
| README 继承父目录名 |
|
| 普通文件,模块名为 |
注意:
/pages.md与/pages/README.md不能同时存在,因为二者都映射到vault::pages。
引用语法
采用 wiki link 扩展:
[[intro]] # 当前命名空间下的 Module `intro`
[[#intro]] # 当前 Module 内的 block `#intro`
[[pages]] # Module `pages`(即 `/pages/` 目录层级的 README)
[[pages::intro]] # 相对路径:Module `pages` 下的子模块 `intro`
[[pages#intro]] # Module `pages` 内的 block `#intro`
[[pages::intro#section]] # Module `intro` 中的 block `#section`
[[super::intro]] # 父模块下的 Module `intro`
[[super]] # 父模块本身
[[vault::pages::intro]] # 绝对路径:仓库根下的 Module `intro`
[[vault::pages::intro#section]] # 绝对路径到 block `#section`规则:
vault::表示从仓库根开始的绝对路径,vault为保留根模块别名。super::表示父模块路径,可链式使用(如super::super::foo)。无
#表示引用 Module。有
#表示引用目标 Module 内的 block label。[[intro]]与 block label 冲突时,优先解析为 Module。同一 Module 内 label 不允许重复;重复时 LSP 报 diagnostic 错误。
不支持的扩展
self:::当前模块显式引用。暂不提供,因为[[xxx]]已可引用当前模块下的子模块,[[#xxx]]已可引用当前模块内的 block label。
Scope 与属性标注
Inline Scope
这是普通段落,其中 #[#concept,draft,.highlight,priority=high]{关键概念} 需要被标记。Block Scope
#[#intro,draft,.lead,status=draft]{
这是一个 block scope,
可以跨多行、包含标题、列表、代码块。
}多个属性也可拆成多行(类似 Rust):
#[#intro]
#[draft]
#[.lead]
#[status=draft]
{
这是一个 block scope。
}Page 级属性
文件顶部使用 #![...] 声明整个 Module 的属性:
#![#index,math,.public,title=About,status=draft]
# About
正文开始...Scope 与 Block 的关系
Scope 是语义标注层概念,由用户通过
#[...]{}显式定义。Block 是索引/向量化层概念,由解析器按 Markdown 结构隐式切分(段落、章节、代码块、列表等)。
两者是平行关系:一个 Scope 可以包含多个 Block,一个 Block 也可以包含多个 inline Scope。
用户显式标注的 block Scope 优先作为一个独立索引单元;未标注内容按默认 Block 切分。
属性语法
属性列表写在 #[...] 或 #![...] 中,逗号分隔、不加空格。一个 Scope 或 Module 前可连续写多个 #[...] / #![...],效果等价于合并属性列表。
attrs ::= attr ("," attr)*
attr ::= "#" identifier # label(互斥,一个 scope 只能有一个)
| identifier # tag(可多个)
| "." identifier # class(可多个)
| identifier "=" value # key=value 属性示例 #[#intro,draft,.lead,status=draft]{...}:
#intro:labeldraft:tag.lead:classstatus=draft:attribute
解析边界
Scope 的 {} 匹配需满足:
忽略代码块(fenced code block)和行内代码(inline code)以及数学公式中的
{}。结束
}建议位于行首(或仅前导空白),降低与普通文本中}的歧义;inline scope 的}紧跟内容即可。支持嵌套 scope,解析器按括号层级计数。
LSP 集成
Rua 核心通过 LSP 提供以下能力:
Go to Definition:
[[path#label]]跳转到对应 Module 或 block。Find References:查找 Module、label 的所有引用。
Rename Symbol:跨文件重命名 Module、label。
Autocomplete:模块路径与 label 补全。
Diagnostics:未解析引用、重复 label、循环引用等错误。
Hover Info:显示逻辑路径与属性。
与现有 Markdown / Obsidian 的关系
Rua 的
.md文件仍可在 Obsidian、VS Code 等编辑器中打开和编辑。[[vault::pages::intro]]、#[#label]{...}、#![...}等新语法在 Obsidian 中不会被识别为 Rua 语义,但也不会破坏文件读取。YAML frontmatter 作为标准 Markdown/Obsidian 元数据保留,不属于 Rua 语义标注系统;Rua 的
#![...]等标记属于正文范围。Rua 提供独立的渲染/预览层来解析这些扩展。
未决问题
Module 的可见性机制:暂不设计,默认全部公开。
2026-07-04 初始设计 (claude-code, kimi-for-coding)
通过讨论确定以下转向:
放弃 Typst 作为核心格式:Typst 更适合单文档排版,不是知识库模块化的核心需求。本设计退为 Markdown 超集方案,Typst 作为未来扩展方向。
核心问题调整为「文档库的逻辑表示与模块化建模」:借鉴 Rust 模块系统,为 Markdown 仓库建立逻辑模块树。
确定 scope + attribute 语法:采用 Rust-like 属性语法。
#[...]{}用于 scope 级属性,#![...]用于 Module 级属性;属性分为#label(唯一)、baretag、.class、key=value四类。早期曾考虑过[]@xxx、{}@xxx等 attribute-after-scope 语法,因 page 级属性表达 awkward 而改为 Rust-like 前缀属性。确定引用语法:无
#引用 Page/Module,有#引用 block;根模块别名从crate改为vault。统一 Module 与 Page 概念:所有
.md文件都是 Module,不存在独立的 Page 概念;README 文件代表其所在文件夹层级的 Module。确立 README 冲突规则:
xxx.md与xxx/README.md不能共存,二者映射到同一模块路径,与 Rustmod.rs规则一致。*支持
super::引用*:super表示父模块,可用于相对路径引用。区分 Label / Tag / Class / Attribute:
#id为互斥 label,bare token 为聚合 tag,.class为展示类,key=value为属性。Scope 与 Block 平行:Scope 是用户显式标注单元,Block 是索引隐式切分单元;Scope 可跨多个 Block。
*支持多个
#[...]属性*:一个 Scope 前可连续写多个#[...],与 Rust 属性风格一致。YAML frontmatter 独立保留:frontmatter 作为标准 Markdown/Obsidian 元数据,不属于 Rua 语义标注系统;Rua 标记属于正文范围。
关闭剩余未决问题:同一 Module 内 label 重复即报错;Module 可见性暂不设计;
self::由无前缀引用替代。
本设计文档是后续实现 LSP、解析器和毕设论文第 4 章(详细设计)的基础。