🛠️ Rime Patch 方法论:配置修改的终极指南
面对 Rime 庞杂的配置文件,直接修改原方案(Schema)往往会在软件更新后被覆盖。因此,学会写 custom.yaml(补丁文件)是你真正驾驭 Rime 的必经之路。
理解 Patch 的核心,在于明白它覆盖替换的逻辑和路径节点的寻找。本文将帮你理清思路,避开常见的语法陷阱。
⚖️ 一、 权重逻辑与“最终态”认知
1. 权重排序
在 Rime 中,配置文件的生效权重遵循严格的层级:
custom.yaml > schema.yaml > default.yaml
💡 提示: 如果你要修改万象拼音的主方案
wanxiang.schema.yaml,你需要在同级目录创建并修改wanxiang.custom.yaml。 注:词库文件(如dict.yaml或txt文本)是纯数据,不能使用 Patch 机制。
2. 认识编译后的“最终态”
很多时候我们打开主方案,会看到 __include、__patch 或 __append 这样的指令。这些是 Rime 内部的合并与重定向指令。
__include: xxx:将其他段落的内容完整拉取过来。__patch: xxx:通常与__include配合或叠加使用,用于进一步引入另一个段落。__append: xxx:通常在被引用的源文件中使用,用于将列表内容拼接到一起。
🧬 三大魔法指令 (__include, __patch, __append) 演变实战
为了让你彻底看懂 Rime 在后台是如何“拼乐高”的,我们用一个极其简短的例子,演示这三个指令是如何协同工作,并最终演变成“最终态”的。
📦 第一步:定义源文件(零件库)
假设我们有一个专门存放拼写规则的独立文件叫 wanxiang_algebra.yaml。里面定义了两个段落,注意观察 __append 的位置:
# 文件:wanxiang_algebra.yaml
mixed:
通用规则:
- erase/^xx$/ # 规则A:删除xx
全拼附加规则:
__append: # 👈 注意这里:声明我要“追加”,而不是“覆盖”
- derive/^z/zh/ # 规则B:z 派生 zh
🛠️ 第二步:在方案中使用指令(拼装)
现在,我们在自己的补丁文件(或主方案)中,要把上面这两个零件组合进我们的 speller/algebra 里。
# 文件:wanxiang.custom.yaml
patch:
speller/algebra:
__include: wanxiang_algebra:/mixed/通用规则 # 👈 动作 1:把“通用规则”直接拿过来
__patch: wanxiang_algebra:/mixed/全拼附加规则 # 👈 动作 2:把“全拼附加规则”以补丁形式叠加上去
✨ 第三步:编译后的“最终态”(成品)
当你点击“重新部署”后,Rime 的编译器会在后台执行上述指令。如果你打开 build/wanxiang.schema.yaml,你会发现那些带 __ 的魔法指令全都不见了,变成了一个干净、扁平的列表:
# 文件:build/wanxiang.schema.yaml (Rime 真正读取的最终形态)
speller:
algebra:
- erase/^xx$/ # 👈 这是 __include 拿过来的第一部分
- derive/^z/zh/ # 👈 这是 __patch 配合源文件的 __append 追加进来的第二部分
🎯 为什么你要懂这个?
因为当你去阅读主方案源码时,你看到的往往是第二步(拼装态)。
如果你试图去写 speller/algebra/__include: xxx 来修改它,是一定会失败的!
你的 Patch 永远是针对第三步(最终态)去写的。你脑海中必须能把那堆魔法指令“脑补还原”成最终的列表结构,然后再用 @数字 或 @next 去精准修改它。
🛡️ 二、 语法铁律:唯一的 patch:
一个 custom 文件顶层,必须且只能有一个 patch: 声明。
在 YAML 语法中,缩进代表层级(通常是两个空格)。所有你想修改的参数,都必须正确缩进并包裹在这个唯一的 patch: 之下。如果你在文件中间另起一行写了其他不缩进的顶层内容,这会截断整个 Patch 段落,导致后续补丁失效。
(如果确有需要补充的非 Patch 内容,请统一写在文件的最末尾。)
🧮 三、 Patch 语法速查表
我们将 YAML 的层级节点看作“键(Key)”,具体的内容看作“值(Value)”。Rime 提供了以下几种控制路径的语法:
| 操作类型 | 语法示例 | 行为说明 |
|---|---|---|
| 局部修改 | key/subkey: value |
(最常用)修改深层特定项,不影响其他同级项。 |
| 全局替换 | key: new_value |
暴力覆盖,清空并替换整个键下的所有原内容。 |
| 列表追加 | key/+: |
在列表数组的末尾追加新的值。 |
| 特定行替换 | key/@5: value |
替换列表中第 6 行的值(索引从 0 开始计数)。 |
| 末尾行替换 | key/@last: value |
替换列表中最后一行的值。 |
| 特定行插队 | key/@before 5: value |
在第 6 行的位置插入新值,原值依次后移。 |
⚠️ 避坑警告: Rime 的 Patch 机制 不支持使用
/-:来删除列表项!不要写出engine/filters/-: xxx这样的代码,它不会生效。
⚔️ 四、 局部修改 vs 全局替换(致命区别)
在使用 Patch 时,使用 / 划定路径和使用 : 缩进覆盖,结果是天壤之别。
假设原方案中有如下段落:
✅ 正确做法:只改每页候选项数(局部修改)
使用 / 级联,直达目标,其他配置(如 settings)完好无损。
❌ 错误做法:导致整个模块被清空(全局替换)
使用 : 展开并重写。这会告诉 Rime:“把原有的 menu彻底销毁,替换成我现在写的样子”。这会导致 settings 下的字体和颜色配置全部丢失。
🗑️ 五、 如何真正“删除”一个值?
既然不支持 /-:,我们该如何删除某个功能呢?
1. 清空单一键值
如果你想让某个设定完全失效(例如取消自定义短语的用户词库),可以直接给它赋一个空值。部署时,这一行等同于不存在。
2. 删除列表(数组)中的某一行
这是一个难点。因为你无法单独删除列表里的某一项,你只能采用 “全局替换” 的方法,把整个列表重写一遍(在重写时,剔除掉你不想要的那一项)。
patch:
# 暴力重写整个 filters 列表,故意不写你想删除的那个滤镜
engine/filters:
- lua_filter@*chars_filter
- simplifier@emoji
- uniquifier
📍 六、 列表的高级插队操作(以 Engine 为例)
在修改 engine(引擎核心流水线)时,顺序是极其关键的。
如果你瞎用追加指令,把一个引导器放到了最后,它会被前面的组件拦截,导致功能彻底失效。
❌ 错误示范:使用追加 (/+)
这会将自定义翻译器塞到列表最后,极有可能不工作。
✅ 正确示范:使用特定行插队 (@before)
遇到对顺序敏感的组件,必须精准排队。例如,你想把自定义短语翻译器插到第 6 个位置(索引为 5):
patch:
# @before 5 表示插队到第 6 行,原来的第 6 行及后面的元素依次后移
engine/translators/@before 5: table_translator@custom_phrase
@ 语法定位具体行时,后面直接跟值,不需要再加短横线 -。)