emacs-在windows上使用emacs内的rimel进行中文输入
本文全程使用emacs内置的rimel输入法+rime-frost方案写作
Emacs的中文输入一直以来都是一个问题。虽然我们已经有了珠玉在前的sis,可以在Emacs内流畅切换外部输入法,但是在Windows和MacOS上,外部输入法的表现总有一些美中不足:
在Windows上,获取输入法状态的API好像有点问题。根据我个人使用小狼毫的经验来看,当小狼毫在其他程序中切换中英文输入状态时,又或者当你的rime重载后,Emacs往往记住的是先前的状态,导致不得不手动切换一次小狼毫的中英文状态以匹配Emacs内的sis状态。
在MacOS上,问题更加麻烦,广泛使用的Emacs-plus并没有内置的切换输入法API,相反的是GUI Emacs Mac Port有,导致我一直用坛友Lucius的编译脚本为Emacs-plus 打上输入法切换功能的补丁,以适配sis。
而 sis 本身也有一些美中不足。因为它无法判断用户的按键输入是为了在可输入区域内输入文本,还是为了触发一个按键功能,所以在一些时候比较麻烦。比如在 org-agenda 的界面,就可能会出现按下 t 键弹出拼音输入框的问题。但内置的输入法就没有这个困扰,只会在可输入文本的场景出现。
幸好,Emacs内部也能调用动态模组,因此涌现了pyim、liberime、emacs-rime和rimel等充分利用Emacs内置输入功能以及librime的中文输入模组。其中liberime仅用于提供一个动态模组,将Emacs和外部的librime.dll结合起来,因此暂且不将其列入对比。至于剩下的三者,可以在这里查看比较。
我选用 rimel 的原因其实也很简单,单纯是它比较轻量,安装理论上来说相对更容易。我曾经尝试过在Windows 的 Emacs 上安装 emacs-rime ,最终还是被编译动态模块所劝退了。
1. 安装配置 liberime 和 rimel
因为这两者都上了 melpa 且 rimel 依赖着 liberime ,因此我们在写 use-package 的时候仅需要指明 rimel 即可:
(use-package rimel :ensure t
然后我们参考 rimel 和 liberime 给出的说明,进行一番配置。
首先配置 liberime 的 dll 位置以及相关文件夹。
我把 liberime-core.dll 放在了 ~/.emacs.d/bin/ 路径下,以便在多设备下同步。相关的依赖 DLL ,也在同一个位置,我们后面详解有哪些需要复制过来。
:custom
(liberime-module-file "~/.emacs.d/bin/liberime-core.dll")
然后我们设置两个关键的文件夹路径: shared-data-dir 决定了共享的基础 rime 配置文件,每次我们启动 rimel 的时候,都会从这个文件夹和 user-data-dir 读取配置文件,并在 user-data-dir 生成一个直接使用的最终build文件夹。出于一次配置所有设备共享的考虑,我们可以将输入方案放在 shared-data-dir 里面,并将 shared-data-dir 设置到一个跟随 Emacs 配置文件同步的路径中,以便在不同设备中快捷共享配置方案。 user-data-dir 仅用于存储用户个人定制方案和输入数据,我们需要把它放到不同步的路径中(我放在 ~/emacs-local/ 文件夹)。
前者可以先从 liberime 的 release 路径里 /share/rime-data 中复制来。
(liberime-shared-data-dir "~/.emacs.d/bin/rime-data")
(liberime-user-data-dir "~/emacs-local/rimel")
这里记得配置一下默认输入法为 rimel
(default-input-method "rimel")
下面这个变量决定了在什么区域自动关闭输入法进入英文模式。
;; 推荐配置:代码区 + 字母后 + 大写字母
(rimel-disable-predicates
'(rimel-predicate-prog-in-code-p
rimel-predicate-after-alphabet-char-p
rimel-predicate-current-uppercase-letter-p))
这里其实也可以再添加一个 rimel-predicate-org-in-src-block-p ,以便在代码块内自动切换成英文。但是需要注意,它用到的函数 org-in-src-block-p 文档中明确指出:所有在 end 之后的空行都会被计算进 src-block 之内。
Note that affiliated keywords and blank lines after are considered a part of a source block.
所以根据实际情况考虑使用吧。
在 GUI 下使用悬浮框展示候选词
;; 可选:使用 posframe 展示候选(如果安装了 posframe,默认会使用 posframe)
(rimel-show-candidate 'posframe))
2. 安装配置 DLL
注意,由于我用的 UCRT 版本的 Windows Emacs ,因此后续均以次为参考,请读者自行根据 Emacs 版本选择对应 DLL 。
书接上文,如果我们从 liberime 的 release 中直接下载预编译打包好的 DLL 文件,放到前面指明的 liberime-module-file 路径中,会发现根本没办法启动。
根据尝试,这是因为预编译好的文件中仅在 /bin 里有 liberime.dll 和 librime-1.dll ,还缺少这两个 DLL 的依赖文件。
具体依赖文件如下:
libgflags.dll libglog-2.dll libleveldb.dll libmarisa-0.dll libopencc-1.2.dll libunwind.dll libyaml-cpp.dll
最简单的方式就是下载一个msys2,然后安装-配置镜像源-更新,安装一个librime,最后在对应的 版本/bin (比如我是 /ucrt/bin )中找到DLL并拷贝即可。
第二简单的获取方式就是找到 msys2 的 package 界面,搜索需要的文件名,然后点击 「File:」 后面跟随的链接,下载解压,并在 /bin 下找到对应 DLL ,复制出来放到前面指明的 liberime-module-file 路径中即可。
3. 调整 rime 配置方案
假如你以上两步均胜利完成,那么重启 Emacs 之后,轻按 C-\ ,随便按几下键盘,应该就能看见候选词出现在 minibuffer 或者 posframe 中了。但细心的你同样发现,怎么打出来的是繁体字啊?这是因为前面复制的默认使用明月拼音,初始输出就是繁体。幸好,我们可以充分利用 rime 的自定义功能,换成我们想用的方案。
但是在你兴高采烈准备使用各种花里胡哨的方案前,不得不提醒一句:由于我们的 liberime 仅使用了 librime ,因此在许多方案中被广泛使用的 LUA 脚本插件、语言模型插件(octagram)均无法在 liberime 中生效使用,需要我们手动调整方案。
至于使用什么方案,可以参考 rime-frost 的作者搞的 rime-schema 评测,并着重查看其中没有「_with_gram」的结果。根据 2026 年 5 月 12 日的评测结果,rime_frost 句子正确率: 46.88% 、文字正确率: 86.25% 、文字正确率(逐句平均): 84.87% ,排名前列。所以我后续以 rime-frost 方案为例讲解。
我们从 rime-frost 的 release 下载最新方案压缩包,解压之后将里面内容全部复制替换 shared-data-dir 内文件,并直接修改 shared-data-dir 内对应文件。
3.1. 修改默认输入方案
虽然 rimel 提示我们可以通过修改 rimel-schema 变量来修改输入方案,但实践证明没有这个必要,直接修改 default.yaml 就好了。由于我只用小鹤双拼,因此我改为了:
# 方案列表
schema_list:
# 可以直接删除或注释不需要的方案,对应的 *.schema.yaml 方案文件也可以直接删除
- schema: rime_frost_double_pinyin_flypy # 小鹤双拼
然后,进入你选择的方案配置文件中,我这里是 rime_frost_double_pinyin_flypy.schema.yaml ,删除掉其中涉及到 lua 的部分,比如 engine 下,所有以 lua_ 开头的行,都必须删掉。我删除后的结果如下:
# 输入引擎
engine:
processors:
- ascii_composer
- recognizer
- key_binder
- speller
- punctuator
- selector
- navigator
- express_editor
segmentors:
- ascii_segmentor
- matcher
- abc_segmentor
- affix_segmentor@radical_lookup # 部件拆字自定义 tag
- punct_segmentor
- fallback_segmentor
translators:
- punct_translator
- table_translator@frost_aux # ` single-char aux lookup
- script_translator
- table_translator@custom_phrase # 自定义短语 custom_phrase.txt
- table_translator@melt_eng # 英文输入
- table_translator@cn_en # 中英混合词汇
- table_translator@radical_lookup # 部件拆字反查
filters:
- reverse_lookup_filter@radical_reverse_lookup # 部件拆字滤镜
- simplifier@emoji # Emoji
- simplifier@chaifen
- simplifier@chaifen_all
- simplifier@chinese_english
- simplifier@traditionalize # 简繁切换
- simplifier@mars #火星文
- uniquifier # 去重
3.2. 配置同步
因为同步功能只看用户文件夹,所以我们需要进入 user-data-dir 里,修改 installation.yaml ,添加一行 sync_dir: ,才可以利用 rime 的同步功能。
具体方式与小狼毫或者鼠须管一致,这里不详细展开。
3.3. 一些个人偏好的优化
由于 Emacs 的内部界面寸土寸金,我关闭了候选词备注拼音功能,仅需将 translator: 的 comment_format: 设置为 - xform/.*// 即可。
: 下述内容已于这个 commit 里修复,不再需要。
rimel在启动的时候似乎会错误将 rimel-schema 与 (liberime-current-schema) 进行字符串比较,但后者根本不是一个可行的函数。我们需要手动修改 rimel-active 函数,将
(defun rimel-activate (_name)
"Activate rimel input method.
Called by Emacs when user selects the \"rimel\" input method.
_NAME is the input method name (unused)."
(unless (liberime-workable-p)
(liberime-load))
(when (and rimel-schema
(liberime-workable-p)
(not (string= rimel-schema (liberime-current-schema))))
(liberime-try-select-schema rimel-schema))
(setq-local input-method-function #'rimel-input-method)
(setq-local deactivate-current-input-method-function #'rimel-deactivate))
替换为
(defun rimel-activate (_name)
"Activate rimel input method.
Called by Emacs when user selects the \"rimel\" input method.
_NAME is the input method name (unused)."
(unless (liberime-workable-p)
(liberime-load))
(when (and rimel-schema
(liberime-workable-p)
(not (string= rimel-schema liberime-current-schema)))
(liberime-try-select-schema rimel-schema))
(setq-local input-method-function #'rimel-input-method)
(setq-local deactivate-current-input-method-function #'rimel-deactivate))
内置的 rimel-predicate-org-in-src-block-p 因为直接调用 org-in-src-block-p ,导致代码块换行后难以输入中文。我们直接修改一个自己的判断函数,删去跳过换行、空格功能:
(defun my/rimel-predicate-org-in-src-block-p ()
"Return non-nil when point is inside an Org source block."
(and (derived-mode-p 'org-mode)
(fboundp 'org-in-src-block-p)
(let ((element (org-element-at-point)))
(when (org-element-type-p element 'src-block)
(not (or (<= (line-beginning-position)
(org-element-post-affiliated element))
(>= (line-end-position)
(org-with-point-at (org-element-end element)
(point)))))))))
;; 添加进去
(add-to-list 'rimel-disable-predicates 'my/rimel-predicate-org-in-src-block-p)
如果你使用的系统输入法是小狼毫或者鼠须管,可以在对应的 weasel.custom.yaml 或者 squirrel.custom.yaml 中,明确在 Emacs 里切换到英文模式:
下面是weasel
patch:
app_options:
emacs.exe: # 带 .exe 的进程名:Weasel 15.0 及之前版本须小写; PR #1049 合并后释出的版本大小写不敏感
ascii_mode: true # 英文模式
下面是squirrel
patch:
app_options:
org.gnu.Emacs:
ascii_mode: true
no_inline: true
4. 补充说明
4.1. 关于启动报错
如果你一切都配置就绪了,但是发现执行 liberime-load 的时候报错,可能是加载路径的问题,可以考虑将 liberime-module-file 所在路径添加到 Windows 的用户环境变量里,然后重启电脑以重新读取用户环境变量,再启动 Emacs 试试。
4.2. 关于macOS
我也在 macOS 配置了 rimel 。总体和 Windows 上大差不差,仅需从 liberime 的 release 里面下载对应架构的 liberime-core.dylib ,放在路径中,然后 brew install librime 即可。记得配置里分别明确 windows 和 macOS 下的 liberime-module-file 路径。
需要注意一点,我之前设置的 emoji 为 Twitter Emoji ,在按下 e 键的时候报错,取消恢复到 Apple 默认 emoji 之后就正常了。
5. 配置文件
(files--ensure-directory my/desktop-path)
(let ((file "rimel-config.txt"))
(org-babel-tangle-file (buffer-file-name) (concat my/desktop-path file) "elisp")
(format "[[https://blog.prayhand13013.top/%s][一键查看上述配置代码汇总]]" file))