【问题标题】:How to rebind TAB and RET in an Emacs minor mode?如何在 Emacs 次要模式下重新绑定 TAB 和 RET?
【发布时间】:2012-02-26 14:13:27
【问题描述】:

我正在尝试定义我的次要模式,在 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])(在 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 插入一个 bang(正确),TAB 不退出模式(正确),但不插入任何内容(错误),C-TAB产生“未定义键”错误(绝对错误),并且 RET 退出模式(正确),但插入换行符(错误)。我尝试了一个“新”的 emacs(没有加载站点文件和我的 .emacs),所以没有其他代码应该干预(我害怕 yasn-p 以某种方式捕获 TAB 等)这是怎么回事?

【问题讨论】:

    标签: emacs elisp keymapping


    【解决方案1】:

    我相信您可以用(kbd "<tab>")(kbd "<return>") 分别代替“\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)
    ...
    

    【讨论】:

    • 不正确;我想你的意思是(kbd "&lt;tab&gt;)(kbd "&lt;return&gt;)
    • 对。除此之外 - 我不想绑定 C-TAB,我想要 TAB! (我的 C-TAB 也有问题,稍后我将编辑问题以反映这一点。)
    • 哎呀。是的,我不小心添加了Cs。固定。
    • 谢谢,它有效!唯一可悲的是,我不知道(i)为什么它有效而我的解决方案没有,以及(ii)我怎么知道哪些字符串定义了哪些键(我想查看 kbd 宏的来源可能帮助,虽然)。不过还是非常感谢!!!
    • @mbork - 您应该能够使用M-x describe-key (C-h k [your key]) 找出与键关联的字符串。这将在 minibuffer 中为您提供消息[your key] is undefined,或者它会打开一个新的缓冲区来描述与该键关联的函数。无论哪种情况,它都会巧合地告诉您需要传递什么字符串 kbd 来指定该键。
    【解决方案2】:

    问题如下:一些键通过功能键映射进行翻译,只有在键未绑定时才执行翻译。例如。当您点击 TAB 键时,在 GUI 下生成的 [tab] 事件如果没有绑定到 [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)))
    

    【讨论】:

    • 为什么要把overlaysym设置为overlaysym?
    • 刚刚了解到,需要设置符号的值才能使临时地图处于活动状态。
    【解决方案3】:

    字符串“tab”(即您使用"\t" 获得的内容)与有效的键名不对应。您需要 "\C-i" 用于制表符,"\C-j" 用于换行符。

    【讨论】:

    • 我不确定。我也尝试了[?\t],两种语法在键盘映射中都产生了类似(9 . my-command)的东西,所以我想我正确地完成了这部分。无论如何,使用"\C-i" 无论如何都会在键盘映射中提供相同的条目(但它不起作用)。
    猜你喜欢
    • 1970-01-01
    • 2011-03-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-22
    • 2017-09-26
    • 2014-03-05
    • 1970-01-01
    相关资源
    最近更新 更多