繁体   English   中英

Emacs如何将命令绑定到所有键,只有几个键免除?

[英]Emacs how to bind command to all keys, with a few keys exempt?

我想创建一个具有键映射(foo-mode-map)的次模式(foo-mode),但是当用户按下不在(foo-mode-map)中的任何键时,次模式应该退出。 如何将所有其他键绑定到turn-off-foo-mode

编辑:这是我根据所选答案提出的解决方案。 它也接受数字输入。

(defalias 'foo-electric-delete 'backward-kill-word)

(defun foo-mode-quit (&optional arg)
  (interactive)
  (let ((global-binding (lookup-key (current-global-map)
                                    (single-key-description last-input-event))))
    (unless (eq last-input-event ?\C-g)
      (push last-input-event unread-command-events))
    (unless (memq global-binding
                  '(negative-argument digit-argument))
      (foo-mode -1))))

(defvar foo-mode-map
  (let ((map (make-keymap)))
    (set-char-table-range (nth 1 map) t 'foo-mode-quit)
    (define-key map "-" 'negative-argument)
    (dolist (k (number-sequence ?0 ?9))
      (define-key map (char-to-string k) 'digit-argument))
    (define-key map [backspace] 'foo-electric-delete)
    map))

(define-minor-mode foo-mode
  "Toggle Foo mode.
     With no argument, this command toggles the mode.
     Non-null prefix argument turns on the mode.
     Null prefix argument turns off the mode.

     When Foo mode is enabled, the control delete key
     gobbles all preceding whitespace except the last.
     See the command \\[foo-electric-delete]."
  ;; The initial value.
  :init-value nil
  ;; The indicator for the mode line.
  :lighter " Foo"
  ;; The minor mode bindings.
  :keymap foo-mode-map
  :group 'foo)

您必须决定的一件事是,当用户按下另一个键时,您应该只是退出您的模式,如果您应该运行绑定到相关键的命令(例如在incremental-search )。

一种方法是使用pre-command-hook来检查this-command是否是你的命令之一,如果不是这样,则关闭你的模式。

解决方案2:您可以使用set-char-table-range来设置地图中的所有字符。 例如:

(defvar foo-mode-map
  (let ((map (make-keymap)))
    (set-char-table-range (nth 1 map) t 'foo-turn-off-foo-mode)
    ...
    map))

我已经包含了一个完整的工作示例,用于创建具有您想要的行为的次要模式; 关键是在make-keymap创建的make-keymap上使用set-char-table-range ,它创建一个带有完整char-table的密集键映射; 在使用make-sparse-keymap创建make-sparse-keymap它将不起作用。

(defalias 'foo-electric-delete 'backward-kill-word)

(defun foo-mode-quit (&optional arg)
  (interactive)
  (foo-mode -1))

(defvar foo-mode-map
  (let (map (make-keymap))
    (set-char-table-range (nth 1 map) t 'foo-mode-quit)
    (define-key map [backspace] 'foo-electric-delete)
    map))

(define-minor-mode foo-mode
  "Toggle Foo mode.
     With no argument, this command toggles the mode.
     Non-null prefix argument turns on the mode.
     Null prefix argument turns off the mode.

     When Foo mode is enabled, the control delete key
     gobbles all preceding whitespace except the last.
     See the command \\[foo-electric-delete]."
  ;; The initial value.
  :init-value nil
  ;; The indicator for the mode line.
  :lighter " Foo"
  ;; The minor mode bindings.
  :keymap foo-mode-map
  :group 'foo)

(defvar major-baz-mode-map '(keymap (t . major-baz-mode-default-function)))

为主模式映射设置默认绑定更容易,我在此处包含此示例,但正如我上面提到的,这种备用键映射不适用于次要模式:

(defvar major-baz-mode-map '(keymap (t . major-baz-mode-default-function)))

这在Keymaps格式的文档中讨论,其中说:

(t . binding)

This specifies a default key binding; any event not bound by other
elements of the keymap is given binding as its binding. Default
bindings allow a keymap to bind all possible event types without
having to enumerate all of them. A keymap that has a default binding
completely masks any lower-precedence keymap, except for events
explicitly bound to nil (see below).

答案3:

我会说选择的解决方案太复杂了。 开始操纵事件队列不是您通常想要做的事情。

相反,我建议采用完全不同的解决方案。 如果你始终打开模式,你可以将像backspace这样的绑定到一个函数,该函数可以自己确定它是应该逐个字符还是单词操作。

以下是一个简单的概念验证。 它仍然具有进入单词模式的明确功能,但可以将其推广到既能启用单词模式又能执行某种操作的功能。

(defun foo-electric-enabled nil
  "Non-nil when foo-mode operate in word mode.")

(defun foo-electric-delete (&optional arg)
  (interactive "p")
  (if (and foo-electric-enabled
           (memq last-command '(kill-region
                                foo-electric-delete
                                foo-enable-word-operations)))
      (call-interactively 'backward-kill-word)
    (setq foo-electric-enabled nil)
    (call-interactively 'backward-delete-char-untabify)))

(defvar foo-mode-map
  (let ((map (make-sparse-keymap)))
    (define-key map [backspace] 'foo-electric-delete)
    map))

(defun foo-enable-word-operations ()
  (interactive)
  (setq foo-electric-enabled t))

(define-minor-mode foo-mode
  "Toggle Foo mode.
With no argument, this command toggles the mode.
Non-null prefix argument turns on the mode.
Null prefix argument turns off the mode.

When Foo mode is enabled, the backspace key
gobbles all preceding whitespace except the last.
See the command \\[foo-electric-delete]."
  ;; The initial value.
  :init-value nil
  ;; The indicator for the mode line.
  :lighter " Foo"
  ;; The minor mode bindings.
  :keymap foo-mode-map
  :group 'foo)

上面的代码可以增强,但我把它作为读者的练习:

  • 除了检查函数是否是文字列表的成员之外,可能存在模块范围的列表,或者,您可以使用应该被视为您希望保持在字模式中的函数的属性。

  • 像模式线指示器这样的东西总是在开启。 通过使用另一个次模式实现此包,您可以使用第二个拥有模式行指示符并指示包处于字模式,替换foo-electric-enabledfoo-enable-word-operations

  • 函数foo-electric-delete显式调用backword-delete-char-untabify backward-kill-wordbackword-delete-char-untabify 有许多技术可以分别调用绑定到元退格退格的任何内容。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM