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

时间:2012-02-04 00:59:59

标签: emacs elisp keymapping

我正在尝试定义我的次要模式,在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))

(当然,我 将我的keymap放入minor-mode-map-alist。)

虽然空间绑定命令工作正常,但TAB和RET不知道。如果我改变,例如,“\ t”改为“[f11]”,它就可以正常工作。我尝试使用“矢量符号”([?\ t])具有相同的结果(在C-h C-v-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等)发生了什么事?

3 个答案:

答案 0 :(得分:5)

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


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

...
(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)
...

答案 1 :(得分:3)

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

我认为解决这个问题的正确方法是避免[t]绑定,而是以不同的方式实现“任何其他键的退出模式”(我现在的经验法则是:“如果你需要放东西回到未读命令事件,你可能做错了“)。一种方法是使用pre-command-hook来检查`this-command'是否是你的一个,或者是否(this-command-keys-vector)绑定在你的keymap中。在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)))

答案 2 :(得分:0)

字符串“tab”(即"\t"获得的内容)与有效的密钥名称不对应。您需要"\C-i"作为标签,"\C-j"作为换行符。