Emacs Plus 不能用 Ctrl-` 切换 Squirrel 的输入方案

以前用 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 '(?` ?\[ ?\] ?\\ ?/)))

实际建议

  1. 推荐使用 mituharu/emacs-mac
# 通过 Homebrew 安装
brew tap railwaycat/emacsmacport
brew install emacs-mac

# 或通过 MacPorts
sudo port install emacs-mac-app
  1. 如果你必须用官方 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)
  1. 使用外部工具桥接
# 用 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 转发这个键。

1 个赞

AI 简直一派胡言

1 个赞

这个倒是没问题,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

2 个赞

AI 在小众领域的幻觉挺明显的。比如问某个 emacs/neovim 的插件的配置问题,或者你的这个情况问某个 emacs 的发行版的这种 niche 的不能再 niche 的问题,基本都是胡言乱语。包括让 AI 来写配置,也就写个七八分像搭个框架这个程度了,剩下的具体的行为还是得去看文档或者借鉴别人的配置怎么写的。想把 AI 当搜索引擎来用也就只能 JavaScript/Python 这种主流的语言或者大众科普类问题比较合适。