以前用 railwaycat/emacs-mac + Doom Emacs可以的,换到 Emacs Plus 不行了,把 Ctrl- 映射解除了也不行,报错 Ctrl- is undefined.
不是 ctrl-\ 吗?还不行可以试下执行下 toggle-input-method 这个函数
一直是 F4 or Ctrl-`,这俩在 Emacs Plus 里都用不了。
刚看 railwaycat/emacs-mac 29 修正了编译失败,要不回去算了。。。
我理解错了,你是切系统输入法吧。我是切 rime。系统我绑定的是 ctrl+space
ctrl-space 没问题,是激活 Squirrel 后,用 Ctrl-` 唤出 Squirrel 的方案菜单。
AI 说的,换回 railwaycat/emacs-mac 了。
你观察到的现象是正确的!mituharu/emacs-mac(也称为 Emacs Mac Port 或 railwaycat/emacs-mac)与官方 GNU Emacs 在 macOS 上的行为确实不同。以下是详细的技术分析:
核心原因:事件处理架构差异
1. 输入法集成方式不同
mituharu/emacs-mac:
-
使用了 macOS 原生文本输入系统
-
更好的 Cocoa/AppKit 集成
-
通过
NSTextInputClient协议正确处理输入法事件 -
代码位置:
src/mac.c中的mac_handle_text_input_event()
官方 GNU Emacs:
-
使用 X Window 模拟层(通过 ns-win.el)
-
自行处理键盘事件,有时会绕过系统输入法
-
更直接地将按键转换为 Emacs 内部事件
2. 关键技术实现差异
/* mituharu/emacs-mac 中的关键代码片段 */
// 在 mac.c 中:
bool
mac_handle_text_input_event (NSEvent *event)
{
// 正确处理输入法提交的文本
// 不过度拦截组合键
if ([event type] == NSTextInput) {
// 让输入法先处理
return handle_text_input([event characters]);
}
return false;
}
3. 事件传递链的区别
官方 GNU Emacs 的事件流:
键盘 → 系统 → X11/XQuartz模拟层 → ns-win.el → Emacs 内部事件
mituharu/emacs-mac 的事件流:
键盘 → 系统 → Cocoa/AppKit框架 → 原生输入法 → Emacs Mac Port → Emacs
具体实现细节对比
官方 GNU Emacs 的问题代码:
;; ns-win.el 中的相关逻辑
(defun ns-encode-event (event)
"在官方 Emacs 中,C-` 可能被过早捕获"
(when (and (equal (event-basic-type event) ?`)
(memq 'control (event-modifiers event)))
;; 这里可能直接处理,不传递给输入法
(internal-magic-key-handler event)))
mituharu/emacs-mac 的改进:
// 在 Emacs Mac Port 中:
void
mac_send_event (struct mac_display_info *dpyinfo, NSEvent *event)
{
NSEventType type = [event type];
// 特殊处理输入法相关事件
if (type == NSTextInput ||
(type == NSKeyDown && [event hasMarkedText])) {
// 如果有标记文本(输入法正在输入),让输入法处理
[self inputContext] handleEvent:event];
return;
}
// 普通按键处理
if ([event modifierFlags] & NSEventModifierFlagControl) {
unichar c = [[event charactersIgnoringModifiers] characterAtIndex:0];
if (c == '`') {
// 更智能的处理:检查是否在输入法上下文中
if (![self hasMarkedText]) {
process_emacs_event(dpyinfo, event);
}
}
}
}
为什么 mituharu/emacs-mac 能正确处理?
1. 更好的 Cocoa 集成
-
实现了完整的
NSTextInputClient协议 -
支持 marked text(预编辑文本)的概念
-
与 macOS 输入法框架深度集成
2. 更智能的事件过滤
// 检测是否应该让输入法处理
- (BOOL)shouldBeHandledByInputMethod:(NSEvent *)event
{
// 如果用户开启了输入法,且不是Emacs的专用快捷键
if ([self hasActiveInputMethod] &&
![self isEmacsReservedKey:event]) {
return YES;
}
return NO;
}
3. 保留了系统快捷键处理
mituharu 的版本默认允许系统级快捷键(包括输入法切换)正常工作。
解决方案的启示
如果你必须使用官方 GNU Emacs,可以从 mituharu/emacs-mac 的实现中学到:
1. 修改 ns-win.el 的补丁方案
;; 模拟 mituharu 版本的行为
(defadvice ns-encode-event (around allow-input-method activate)
"让特定组合键通过,不拦截"
(if (and (equal (event-basic-type event) ?`)
(memq 'control (event-modifiers event))
(current-input-method)) ; 有输入法激活时
(progn
;; 不处理,返回 nil 让系统处理
(setq ad-return-value nil))
ad-do-it))
2. 使用 Emacs Mac Port 的配置技巧
如果你切换到了 mituharu/emacs-mac,这些配置更有用:
;; 在 Emacs Mac Port 中更好的输入法支持
(setq mac-pass-command-to-system nil
mac-pass-control-to-system nil
mac-pass-option-to-system nil
mac-pass-control-command-to-system nil)
;; 但允许特定的 Ctrl+` 传递
(setq mac-pass-control-to-system
(append mac-pass-control-to-system '(?` ?\[ ?\] ?\\ ?/)))
实际建议
- 推荐使用 mituharu/emacs-mac
# 通过 Homebrew 安装
brew tap railwaycat/emacsmacport
brew install emacs-mac
# 或通过 MacPorts
sudo port install emacs-mac-app
- 如果你必须用官方 Emacs,可以使用这个变通方案:
;; 完全禁用 C-` 在 Emacs 中的处理
(defun disable-ctrl-backquote ()
(define-key input-decode-map (kbd "C-`") nil)
(define-key local-function-key-map (kbd "C-`") nil)
(define-key key-translation-map (kbd "C-`") nil))
(add-hook 'after-init-hook 'disable-ctrl-backquote)
- 使用外部工具桥接
# 用 Hammerspoon 或 Keyboard Maestro 检测
# 当焦点在 Emacs 时,拦截 C-` 并发送模拟按键
技术总结
| 特性 | 官方 GNU Emacs | mituharu/emacs-mac |
|---|---|---|
| 输入法集成 | 模拟层,较差 | 原生 Cocoa,优秀 |
| 快捷键处理 | 可能拦截系统快捷键 | 尊重系统设置 |
| C-` 支持 | 有问题 | 正常 |
| 性能 | 较好 | 优秀(原生渲染) |
| 功能 | 基础 | 额外 macOS 特性 |
结论:mituharu/emacs-mac 采用了更现代的 macOS 集成方法,正确处理了系统级快捷键,而官方 GNU Emacs 仍使用较旧的、与系统集成度较低的架构。如果你的工作流依赖输入法,强烈建议切换到 Emacs Mac Port。
切换 squirrel 方案我不知道 emacs-plus 可以怎么修复,不过如果只是单纯切换squirrel和系统英文输入法的话,可以用emacs-mac移植过来的patch,这样就可以shift切换了。.emacs.d/scripts/macos/patches/emacs-31/ns-mac-input-source.patch at main · LuciusChen/.emacs.d · GitHub
emacs nsport 默认会捕获 C-` 键不转发给输入法。
最简单的 workaround 是先按一个键呼出候选窗再按 C-` 。
当然也可以自己写 patch 转发这个键。
AI 简直一派胡言
这个倒是没问题,Ctrl-Space 在 Emacs Plus 里工作正常。
这个 workaround 确实可行,赞!我找了下 emacs-mac 代码没看出为什么它没问题,然后就卸了 emacs plus 30 换 emacs-mac 29 了,等哪天 emacs-mac 29 挂了我再换回来😀
佬给 Emacs Plus 搞个补丁呗。
以前写过一个特判 C-` 的补丁,也就几行,因为不太普适就没放出来过。之后能摸到 Mac 的时候发论坛上吧。如果要完善解决还得再想想。
这个 patch 是给 emacs 30 用的,31 我不确定能不能 apply,不过手动做相应变更应该不难。
From 0a850f3890e18db3a4dad82c27179a27573f87b0 Mon Sep 17 00:00:00 2001
From: ksqsf <[email protected]>
Date: Sun, 11 Jun 2023 14:29:25 +0800
Subject: [PATCH] Do not pass C-` to Emacs.
---
src/nsterm.m | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/src/nsterm.m b/src/nsterm.m
index e98a86cdbdb..2cfc76b5581 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -7081,6 +7081,10 @@ In that case we use UCKeyTranslate (ns_get_shifted_character)
&& (emacs_event->modifiers != shift_modifier)
&& [[theEvent charactersIgnoringModifiers] length] > 0))
{
+ /* for Rime: do not pass C-` to emacs, but to interpretKeyEvents. */
+ if (code == 0x60)
+ goto system_interpretation;
+
emacs_event->kind = NON_ASCII_KEYSTROKE_EVENT;
/* FIXME: What are the next four lines supposed to do? */
if (code < 0x20)
@@ -7111,6 +7115,8 @@ In that case we use UCKeyTranslate (ns_get_shifted_character)
}
}
+ system_interpretation:
+
/* If we get here, a non-function key without control-like modifiers
was hit. Use interpretKeyEvents, which in turn will call
insertText; see
--
2.51.2
AI 在小众领域的幻觉挺明显的。比如问某个 emacs/neovim 的插件的配置问题,或者你的这个情况问某个 emacs 的发行版的这种 niche 的不能再 niche 的问题,基本都是胡言乱语。包括让 AI 来写配置,也就写个七八分像搭个框架这个程度了,剩下的具体的行为还是得去看文档或者借鉴别人的配置怎么写的。想把 AI 当搜索引擎来用也就只能 JavaScript/Python 这种主流的语言或者大众科普类问题比较合适。