如何在 Emacs 次要模式下重新绑定 TAB 和 RET?

发布于 2025-01-02 03:45:21 字数 1767 浏览 4 评论 0原文

我正在尝试定义我的次要模式,在 isearch-mode 之后模仿它(因为它是一种交互式搜索和替换工具,我认为这可能是一个很好的起点)。我的命令运行良好(在全局键绑定上进行了测试),但在将它们本地(在次要模式映射中)绑定到某些键(即 TAB 和 RET)时遇到严重问题。我正在做这样的事情:(

(defvar my-mode-map
  (let ((map (make-sparse-keymap)))
    (define-key map "\s" 'my-command)
    (define-key map "\t" 'another-one)
    (define-key map "\r" 'yet-another)
    map))

当然,我确实将我的键盘映射放入minor-mode-map-alist中。)

虽然空间限制命令工作正常,但TAB和RET不知何故却不能。如果我将“\t”更改为“[f11]”,它就可以正常工作。我尝试使用“矢量符号”([?\t]) 获得相同的结果(在 Ch Cv-ing 我的键盘映射之后,这并不奇怪)。可能会发生什么?

编辑:为了澄清问题,我尝试隔离它并提出了以下代码。假设我想要一个人为的、相当简单的次要模式 tabbang,其中 TAB 键插入感叹号。我正在这样做:

(defvar tabbang-mode)
(add-to-list 'minor-mode-alist '(tabbang-mode tabbang-mode) t)

(defvar tabbang-mode-map
  (let ((map (make-sparse-keymap)))
    (define-key map [?\t] 'tabbang-insert-bang)
    (define-key map [?\C-\t] 'tabbang-insert-bang)
    (define-key map [f11] 'tabbang-insert-bang)
    (define-key map [?\r] 'tabbang-done)
    (define-key map [t] 'tabbang-other-char)
    map))
(add-to-list 'minor-mode-map-alist `(tabbang-mode . ,tabbang-mode-map) t)

(defun tabbang-insert-bang ()
  (interactive)
  (insert "!"))

(defun tabbang-mode ()
  (interactive)
  (setq tabbang-mode " tabbang"))

(defun tabbang-other-char ()
  (interactive)
  (tabbang-done)
  (setq unread-command-events
    (append (listify-key-sequence (this-command-keys))
        unread-command-events)))

(defun tabbang-done ()
  (interactive)
  (setq tabbang-mode nil))

在我的 tabbang-mode 中,“其他”键正确退出模式并插入自身,f11 插入感叹号(正确),TAB 不退出模式(正确),但不插入任何内容(错误),C-TAB 产生“未定义键”错误(绝对错误),RET 退出模式(正确),但插入换行符(错误)。我尝试了一个“新鲜”的 emacs(不加载站点文件和我的 .emacs),这样就不会有其他代码介入(我担心 yasnippet 以某种方式捕获 TAB 等)发生了什么事?

I am trying to define my minor mode, mimicking it after isearch-mode (since it is kind of an interactive search-and-replace tool, I thought it might be a good starting point). My commands work well (tested on global keybindings), but I have serious problems with binding them locally (in the minor mode map) to some keys, namely TAB and RET. I'm doing something like this:

(defvar my-mode-map
  (let ((map (make-sparse-keymap)))
    (define-key map "\s" 'my-command)
    (define-key map "\t" 'another-one)
    (define-key map "\r" 'yet-another)
    map))

(Of course, I did put my keymap into minor-mode-map-alist.)

While the space-bound command works fine, TAB and RET somehow do not. If I change, eg, "\t" to "[f11]", it works fine. I tried using the "vector notation" ([?\t]) with identical results (after C-h C-v-ing my keymap it was not surprising). What might be happening?

Edit: to clarify the problem, I tried to isolate it and I came up with the following code. Assume that I want to have an artificial, rather minimal minor mode tabbang in which the TAB key inserts an exclamation mark. I'm doing this:

(defvar tabbang-mode)
(add-to-list 'minor-mode-alist '(tabbang-mode tabbang-mode) t)

(defvar tabbang-mode-map
  (let ((map (make-sparse-keymap)))
    (define-key map [?\t] 'tabbang-insert-bang)
    (define-key map [?\C-\t] 'tabbang-insert-bang)
    (define-key map [f11] 'tabbang-insert-bang)
    (define-key map [?\r] 'tabbang-done)
    (define-key map [t] 'tabbang-other-char)
    map))
(add-to-list 'minor-mode-map-alist `(tabbang-mode . ,tabbang-mode-map) t)

(defun tabbang-insert-bang ()
  (interactive)
  (insert "!"))

(defun tabbang-mode ()
  (interactive)
  (setq tabbang-mode " tabbang"))

(defun tabbang-other-char ()
  (interactive)
  (tabbang-done)
  (setq unread-command-events
    (append (listify-key-sequence (this-command-keys))
        unread-command-events)))

(defun tabbang-done ()
  (interactive)
  (setq tabbang-mode nil))

While in my tabbang-mode, "other" keys correctly exit the mode and insert themselves, f11 inserts a bang (correct), TAB does not exit the mode (correct), but inserts nothing (wrong), C-TAB yields "undefined key" error (definitely wrong), and RET exits the mode (correct), but inserts a newline (wrong). And I tried on a "fresh" emacs (without loading site-file and my .emacs), so that no other code should intervene (I was afraid of yasnippet somehow capturing TAB etc.) What is going on?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

尸血腥色 2025-01-09 03:45:21

我相信您可以使用 (kbd "")(kbd "") 代替“\t”和“\”来更改它们r”分别。


作为对您的编辑的回应,是的,以下内容非常适合我:

...
(defvar tabbang-mode-map
  (let ((map (make-sparse-keymap)))
    (define-key map (kbd "<tab>") 'tabbang-insert-bang)
    (define-key map (kbd "<C-tab>") 'tabbang-insert-bang)
    (define-key map (kbd "<f11>") 'tabbang-insert-bang)
    (define-key map (kbd "<return>") 'tabbang-done)
    (define-key map (kbd "t") 'tabbang-other-char)
    map))
(add-to-list 'minor-mode-map-alist `(tabbang-mode . ,tabbang-mode-map) t)
...

I believe you can change them with (kbd "<tab>") and (kbd "<return>") in place of "\t" and "\r" respectively.


In response to your edit, yes, the following works perfectly for me:

...
(defvar tabbang-mode-map
  (let ((map (make-sparse-keymap)))
    (define-key map (kbd "<tab>") 'tabbang-insert-bang)
    (define-key map (kbd "<C-tab>") 'tabbang-insert-bang)
    (define-key map (kbd "<f11>") 'tabbang-insert-bang)
    (define-key map (kbd "<return>") 'tabbang-done)
    (define-key map (kbd "t") 'tabbang-other-char)
    map))
(add-to-list 'minor-mode-map-alist `(tabbang-mode . ,tabbang-mode-map) t)
...
一抹苦笑 2025-01-09 03:45:21

问题如下:一些键通过 function-key-map 进行转换,该函数仅在键未绑定时才执行转换。例如,如果没有绑定到 [tab],则当您点击 TAB 键时在 GUI 下生成的 [tab] 事件将被转换为 [?\t]。 [return] 也是如此,它被映射到 [?\r]。现在的问题是,您的包罗万象的 [t] 绑定意味着任何键序列都有绑定,因此 [tab] 不再重新映射到 [?\t] 。
isearch 也遇到同样的问题,请参阅我们在 isearch-other-meta-char 中所做的扭曲。

我认为解决这个问题的正确方法是避免 [t] 绑定,而是以不同的方式实现“任何其他键的退出模式”(现在我的经验法则是:“如果您需要将内容放回未读状态) -命令事件,你可能做错了”)。一种方法是使用 pre-command-hook 来检查“this-command”是否是您的命令之一,或者 (this-command-keys-vector) 是否绑定在您的键盘映射中。在 Emacs-24 中,我们可能会使用类似下面的代码来实现这些用途:

(defun set-temporary-overlay-map (map &optional keep-pred)
  (let* ((clearfunsym (make-symbol "clear-temporary-overlay-map"))
         (overlaysym (make-symbol "t"))
         (alist (list (cons overlaysym map)))
         (clearfun
          `(lambda ()
             (unless ,(cond ((null keep-pred) nil)
                            ((eq t keep-pred)
                             `(eq this-command
                                  (lookup-key ',map
                                              (this-command-keys-vector))))
                            (t `(funcall ',keep-pred)))
               (remove-hook 'pre-command-hook ',clearfunsym)
               (setq emulation-mode-map-alists
                     (delq ',alist emulation-mode-map-alists))))))
    (set overlaysym overlaysym)
    (fset clearfunsym clearfun)
    (add-hook 'pre-command-hook clearfunsym)
    (push alist emulation-mode-map-alists)))

The issue is the following: some keys get translated via function-key-map, which only performs the translation when the key would otherwise be unbound. E.g. the [tab] event generated under GUIs when you hit to the TAB key, gets translated to [?\t] if there is no binding to [tab]. Same thing for [return], which gets mapped to [?\r]. Now the problem is that your catchall [t] binding means that any key sequence has a binding, so [tab] is not remapped to [?\t] any more.
isearch suffers from the same problem, see the contortions we do in isearch-other-meta-char.

I think the right approach to this problem is to avoid the [t] binding and instead implement the "exit mode for any other key" in a different way (my rule of thumb nowadays is: "if you need to put things back on unread-command-events, you're probably doing it wrong"). One way to do it is to use a pre-command-hook that checks whether `this-command' is one of yours, or whether (this-command-keys-vector) is bound in your keymap. In Emacs-24, we'll probably have something like the code below for those kinds of uses:

(defun set-temporary-overlay-map (map &optional keep-pred)
  (let* ((clearfunsym (make-symbol "clear-temporary-overlay-map"))
         (overlaysym (make-symbol "t"))
         (alist (list (cons overlaysym map)))
         (clearfun
          `(lambda ()
             (unless ,(cond ((null keep-pred) nil)
                            ((eq t keep-pred)
                             `(eq this-command
                                  (lookup-key ',map
                                              (this-command-keys-vector))))
                            (t `(funcall ',keep-pred)))
               (remove-hook 'pre-command-hook ',clearfunsym)
               (setq emulation-mode-map-alists
                     (delq ',alist emulation-mode-map-alists))))))
    (set overlaysym overlaysym)
    (fset clearfunsym clearfun)
    (add-hook 'pre-command-hook clearfunsym)
    (push alist emulation-mode-map-alists)))
乄_柒ぐ汐 2025-01-09 03:45:21

字符串“tab”(即通过 "\t" 得到的内容)与有效的键名称不对应。您需要类似 "\Ci" 的制表符和 "\Cj" 的换行符。

The string "tab" (that is, what you get with "\t") does not correspond to a valid key name. You want something like "\C-i" for tab and "\C-j" for newline.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文