我想试试看 emacs-rime,但是我需要给他单独配置一个激活的按键么?这样就感觉好像有点割裂,其他地方使用一个按键激活,emacs 里面使用另一个。有没有办法统一呢?我在 mac 使用 rime.
我用 better touch tool 设置了【在非 emacs/ghostty 中按 C-SPC 时切换输入法,否则传递 C-SPC 给应用】
这样就全局统一用 C-SPC 切换了
karabiner-elements 可以让你对相同的快捷键在 emacs 里和 emacs 外进行不同的映射,我是把 Shift 键映射了。同时限制在 emacs 里系统输入法强制切换到英文输入法,这点用 hammperspoon 和 karabiner-elements 都可以做到,也有其它类似的软件。
如果觉得用外部改键麻烦,也可以考虑 key-echo。
emacs中输入法不是有统一输入法切换键C-\吗?
是的 我也是用的 karabiner-elements , 它可以根据当前系统输入法的状态,当前激活的app 是什么 对键做映射 通 过 frontmost_application_unless frontmost_application_if input_source_if input_source_unless 等对当前状态进行判断 ,符合条件才执行 key 映射。
squirrel.custom.yaml:
patch:
app_options:
com.google.Chrome:
# 規避 https://github.com/rime/squirrel/issues/435
inline: true
ru.keepcoder.Telegram:
# 規避 https://github.com/rime/squirrel/issues/475
inline: true
org.gnu.Emacs:
ascii_mode: true
no_inline: true
mac下直接全局切鼠须管就好了吧。。。挺好使
在macOS上不用 emacs-rime,直接使用鼠须管体验和系统一致。 通过 sis 实现自动在 C-x 是切为英文。
我的解法比较暴力,我把系统的输入法切换快捷键设置为 C-\ (emacs 的快捷键),然后 emacs 里面的改为 shift ,我假想 emacs 始终开着输入法,只是通过 shift 切换 ascii 模式。
有点掩耳盗铃,但逻辑上能说通…… ![]()
我不是使用 mac 的,在 linux 上我是直接在启动方式里面修改 emacs 的环境变量禁用系统输入法,只在 emacs 中使用 emacs-rime。
env XMODIFIERS= GTK_IM_MODULE=none emacs
感谢各位的思路。我最后搞定了。但是用了几天用不下去了,主要是因为 rime 在 emacs 里面会崩溃。崩溃后我的输入都丢了,不敢继续用了。我也不知道是不是因为我在使用 emacs31+ghostel的缘故。换回 Squirrel 了。
使用 Karabiner-Elements + Hammerspoon 组合,我的最佳实践
我的配置 https://github.com/SLin0218/nix-config.git
我习惯使用 Capslock + space 切换输入法,在 Emacs 中映射为 Ctrl + \。
{
"conditions": [
{
"bundle_identifiers": ["^org\\.gnu\\.Emacs$"],
"type": "frontmost_application_if"
}
],
"from": {
"key_code": "spacebar",
"modifiers": {
"mandatory": [
"right_command",
"right_control",
"right_option",
"right_shift"
]
}
},
"to": [
{
"key_code": "backslash",
"modifiers": ["left_control"]
}
],
"type": "basic"
}
在 Emacs 始终保持为默认输入法 ABC 避免冲突。并且会记住当前聚焦 App 的输入法,进行自动切换。
---@diagnostic disable: undefined-global
--- === IM Auto Switch ===
local obj = {}
obj.__index = obj
-- Metadata
obj.name = "IM"
obj.version = "1.1.0"
obj.author = "DengShiLin <[email protected]>"
-- 1. 用于记录每个 App 选择的输入法,刚开始为空
obj.appInputSources = {}
obj.cjk = "im.rime.inputmethod.Squirrel.Hans"
-- 固定输入法的 App 规则(在 init() 中动态初始化,以避免 Hammerspoon 启动时系统输入法列表未加载完毕的问题)
obj.fixedApps = {}
-- 面板/代理类应用(这些应用在获得和失去焦点时不会触发系统的 applicationWatcher)
obj.panelApps = {
["Raycast"] = true,
["Alfred"] = true,
["Spotlight"] = true,
["Search"] = true,
}
-- 调试日志开关
obj.debug = false
-- 辅助函数:输出调试日志
local function logDebug(fmt, ...)
if obj.debug then
print(string.format("[IM] " .. fmt, ...))
end
end
-- 统一的自动切换输入法逻辑
function obj:switchInputForApp(appName)
if not appName then
return
end
local targetInput = obj.fixedApps[appName] or obj.appInputSources[appName] or obj.defaultLayout
local currentInput = hs.keycodes.currentSourceID()
if currentInput ~= targetInput then
logDebug("Auto Switch (%s): %s -> %s", "Focus/Activate", appName, targetInput)
local success = hs.keycodes.currentSourceID(targetInput)
if not success then
print(string.format("[IM] Warning: Failed to switch input source to %s", targetInput))
end
end
end
-- 监听 App 激活事件(常规应用)
local function applicationWatcher(appName, eventType, _)
if eventType == hs.application.watcher.activated then
obj:switchInputForApp(appName)
end
end
-- 监听输入法切换事件(订阅系统通知,比 hs.keycodes.inputSourceChanged 更稳定)
local function inputSourceCallback(_, _, _)
-- 优先获取 frontmostApplication (避免调用慢速的 accessibility 接口)
local frontApp = hs.application.frontmostApplication()
local frontAppName = frontApp and frontApp:name()
-- 如果是面板类特殊应用,或者无法获取 frontmostApp 名字,则降级获取 focusedWindow (较慢)
local focusedApp = nil
local focusedAppName = nil
if not frontAppName or obj.panelApps[frontAppName] then
local focusedWindow = hs.window.focusedWindow()
focusedApp = focusedWindow and focusedWindow:application()
focusedAppName = focusedApp and focusedApp:name()
end
logDebug("Debug Focus: focusedWindowApp=%s, frontmostApp=%s", focusedAppName or "nil", frontAppName or "nil")
-- 优先通过当前获得键盘焦点的窗口来获取当前活动 App
local activeApp = focusedApp or frontApp
if activeApp then
local appName = activeApp:name()
if appName then
-- 如果是 Raycast、Alfred 等特殊面板应用,我们不记录其输入法状态(避免污染前台应用记录)
-- 而是开启一个临时的轻量级监听定时器,等它失焦后切回先前活动 App 的输入法
if obj.panelApps[appName] then
if not obj.panelFocusTimer then
logDebug("Started temporary focus timer for panel app: %s", appName)
obj.panelFocusTimer = hs.timer.doEvery(0.1, function()
local fApp = hs.application.frontmostApplication()
local currentAppName = fApp and fApp:name()
if not currentAppName or obj.panelApps[currentAppName] then
local w = hs.window.focusedWindow()
local app = w and w:application()
local name = app and app:name()
if name then
currentAppName = name
end
end
-- 如果焦点已经不是这些面板应用了,说明面板已关闭或失焦
if currentAppName and not obj.panelApps[currentAppName] then
logDebug("Panel app lost focus. Restoring input source for active app: %s", currentAppName)
obj:switchInputForApp(currentAppName)
-- 停止并清理定时器
if obj.panelFocusTimer then
obj.panelFocusTimer:stop()
obj.panelFocusTimer = nil
end
end
end)
end
else
-- 正常应用,如果不是固定输入法的 App,则记录其输入法变更
if not obj.fixedApps[appName] then
local currentInput = hs.keycodes.currentSourceID()
-- 如果当前记录值与实际输入法不一致,更新并记录
if obj.appInputSources[appName] ~= currentInput then
obj.appInputSources[appName] = currentInput
logDebug("Recorded: %s -> %s", appName, currentInput)
end
end
end
end
end
end
function obj:init()
-- 获取默认布局 (增加 nil 安全检查与 fallback)
local layouts = hs.keycodes.layouts(true)
obj.defaultLayout = (layouts and #layouts > 0) and layouts[1] or "com.apple.keylayout.ABC"
-- 设置默认的固定输入法规则
local defaultFixedApps = {
["微信"] = obj.cjk,
["WeChat"] = obj.cjk,
["WeLink"] = obj.cjk,
["Emacs"] = obj.defaultLayout,
["IntelliJ IDEA"] = obj.defaultLayout,
}
-- 将默认规则合并到 obj.fixedApps 中(不覆盖用户已有的设置)
obj.fixedApps = obj.fixedApps or {}
for k, v in pairs(defaultFixedApps) do
if obj.fixedApps[k] == nil then
obj.fixedApps[k] = v
end
end
-- 创建 App 监听器 (绑定在 obj 上以防止垃圾回收)
obj.appWatcher = hs.application.watcher.new(applicationWatcher)
-- 创建输入法变更监听器 (绑定在 obj 上以防止垃圾回收)
obj.inputWatcher = hs.distributednotifications.new(
inputSourceCallback,
"com.apple.Carbon.TISNotifySelectedKeyboardInputSourceChanged"
)
-- 自动启动
obj:start()
end
function obj:start()
if obj.appWatcher then
obj.appWatcher:start()
end
if obj.inputWatcher then
obj.inputWatcher:start()
end
return self
end
function obj:stop()
if obj.appWatcher then
obj.appWatcher:stop()
end
if obj.inputWatcher then
obj.inputWatcher:stop()
end
if obj.panelFocusTimer then
obj.panelFocusTimer:stop()
obj.panelFocusTimer = nil
end
return self
end
return obj
可以试试rimel:
设计哲学
Rimel — 极简主义
- 一个文件、一个依赖、无 C 代码
- 使用
read-event循环,与 Emacs 内置 quail 完全相同的模式 - 不借助 pre-command-hook/post-self-insert-hook 等hook
- 合成过程自包含:函数进入时开始,返回时结束,无状态泄露
- 适合想要最简单可用的 rime 输入法的用户
emacs-rime — 功能完备
- 自带 C 动态模块(lib.c),独立于 liberime
- 支持 5 种候选展示方式(minibuffer/message/popup/posframe/sidewindow)
- 支持 predicates 系统(自动根据上下文切换中英文)
- 支持 inline ASCII 模式
- 使用 minor mode +
overriding-terminal-local-map处理合成期间按键 - 适合需要深度定制和高级功能的用户
pyim + pyim-liberime — 框架化
- pyim 是完整的输入法框架,liberime 只是其中一个后端
- 支持多种输入方案(全拼、双拼、五笔等),rime 只是其一
- 有独立的词频管理、词库、云输入等功能
- pyim-liberime 仅 326 行,本质是
liberime-search()的薄包装 - 适合需要多输入法统一管理的用户
我用了这个 rimel,就是它崩溃的。Claude 说是因为 childframe 光标跟随对位置断言导致的崩溃。给出的建议是使用 echo area 但是用了一会也不习惯。
emacs-rime 和 rimel 是两个不同的包,确定用的是同一个?
两个都用了。
有报错内容吗