跳转至

🛠️ Rime Patch 方法论:配置修改的终极指南

面对 Rime 庞杂的配置文件,直接修改原方案(Schema)往往会在软件更新后被覆盖。因此,学会写 custom.yaml(补丁文件)是你真正驾驭 Rime 的必经之路。

理解 Patch 的核心,在于明白它覆盖替换的逻辑和路径节点的寻找。本文将帮你理清思路,避开常见的语法陷阱。


⚖️ 一、 权重逻辑与“最终态”认知

1. 权重排序

在 Rime 中,配置文件的生效权重遵循严格的层级: custom.yaml > schema.yaml > default.yaml

💡 提示: 如果你要修改万象拼音的主方案 wanxiang.schema.yaml,你需要在同级目录创建并修改 wanxiang.custom.yaml注:词库文件(如 dict.yamltxt 文本)是纯数据,不能使用 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 时,使用 / 划定路径和使用 : 缩进覆盖,结果是天壤之别。

假设原方案中有如下段落:

menu:
  page_size: 10
  settings:
    font_size: 14
    color: blue

✅ 正确做法:只改每页候选项数(局部修改) 使用 / 级联,直达目标,其他配置(如 settings)完好无损。

patch:
  menu/page_size: 5

❌ 错误做法:导致整个模块被清空(全局替换) 使用 : 展开并重写。这会告诉 Rime:“把原有的 menu彻底销毁,替换成我现在写的样子”。这会导致 settings 下的字体和颜色配置全部丢失。

patch:
  menu:
    page_size: 5


🗑️ 五、 如何真正“删除”一个值?

既然不支持 /-:,我们该如何删除某个功能呢?

1. 清空单一键值

如果你想让某个设定完全失效(例如取消自定义短语的用户词库),可以直接给它赋一个空值。部署时,这一行等同于不存在。

patch:
  custom_phrase/user_dict:
  custom_phrase/initial_quality:

2. 删除列表(数组)中的某一行

这是一个难点。因为你无法单独删除列表里的某一项,你只能采用 “全局替换” 的方法,把整个列表重写一遍(在重写时,剔除掉你不想要的那一项)。

patch:
  # 暴力重写整个 filters 列表,故意不写你想删除的那个滤镜
  engine/filters:
    - lua_filter@*chars_filter                     
    - simplifier@emoji                            
    - uniquifier   

📍 六、 列表的高级插队操作(以 Engine 为例)

在修改 engine(引擎核心流水线)时,顺序是极其关键的。 如果你瞎用追加指令,把一个引导器放到了最后,它会被前面的组件拦截,导致功能彻底失效。

❌ 错误示范:使用追加 (/+) 这会将自定义翻译器塞到列表最后,极有可能不工作。

patch:
  engine/translators/+: 
    - table_translator@custom_phrase

✅ 正确示范:使用特定行插队 (@before) 遇到对顺序敏感的组件,必须精准排队。例如,你想把自定义短语翻译器插到第 6 个位置(索引为 5):

patch:
  # @before 5 表示插队到第 6 行,原来的第 6 行及后面的元素依次后移
  engine/translators/@before 5: table_translator@custom_phrase
(注意:使用 @ 语法定位具体行时,后面直接跟值,不需要再加短横线 -。)