Elisp:如何从代码中调用键位映射?

6
以下是一个最简单的代码片段,可以让事情开始运行:
(define-prefix-command 'foo)
(define-key foo "a" 'bar)
(define-key foo "b" 'baz)
(global-set-key (kbd "C-M-r") 'foo)

现在我可以按下C-M-r来“调用”foo键映射。但我想知道如何从代码中实现这个功能,例如: ```elisp (call-keymap foo) ```
(funcall (lambda-from-keymap 'foo))

在调用此函数后,我希望焦点位于迷你缓冲区中,并期望输入abC-h。是否可能实现这样的效果?

3个回答

5
你可以使用 read-key-sequencelookup-key 来实现这个功能:
(defun call-keymap (map &optional prompt)
  "Read a key sequence and call the command it's bound to in MAP."
  ;; Note: MAP must be a symbol so we can trick `describe-bindings' into giving
  ;; us a nice help text.
  (let* ((overriding-local-map `(keymap (,map . ,map)))
         (help-form `(describe-bindings ,(vector map)))
         (key (read-key-sequence prompt))
         (cmd (lookup-key map key t)))
    (if (functionp cmd) (call-interactively cmd)
      (user-error "%s is undefined" key))))

如果你按下C-hread-key-sequence仍然会等待你完成序列。我认为你可以通过循环使用read-key来模拟Emacs的正常行为,但这需要更多的工作量。
使用方法如下:
(defun bar () (interactive) (message "you called bar"))
(defun baz () (interactive) (message "you called baz"))

(define-prefix-command 'foo)
(define-key foo "a" 'bar)
(define-key foo "b" 'baz)
(global-set-key (kbd "C-M-r") 'foo)

(defun call-foo ()
  (interactive)
  ;; Note: pass the symbol form of the keymap so we can give nice help
  (call-keymap 'foo "enter a foo command: "))

(global-set-key (kbd "C-c f") 'call-foo)

啊哈,这只是绑定 help-form 的问题。我还需要更改调用约定,将键位图作为符号要求,以获得漂亮的帮助文本:(call-keymap 'foo) 而不是 (call-keymap foo) - npostavs
我不理解这个(keymap (,map . ,map))是什么意思。 - Stefan
1
@Stefan 嗯,说实话,我不记得当时我在想什么了。看起来它与键位映射元素的(TYPE . BINDING)形式匹配,因此我们假装map中的命令绑定在前缀键MAP下(即应该是一个符号)。显然,当您在read-key-sequence提示处按下C-h时,这会使describe-bindings打印出一些合理的内容。 - npostavs

3

如果键位映射被绑定到一个按键序列,您可以通过设置unread-command-events来模拟该按键序列来调用它:

(setq unread-command-events
      (mapcar (lambda (e) `(t . ,e))
              (listify-key-sequence (kbd "C-M-r"))

+1 因为这是最简单/最容易的解决方案,只要 1) 在当前上下文中将按键映射绑定到已知的按键序列,并且 2) 您可以推迟执行,直到当前命令结束。但是,对于例如 (with-selected-window another-window (run-command-bound-at "C-M-r")) 这样的情况,它将无法工作,因为窗口选择和活动按键映射将在 Emacs 处理 unread-command-events 之前恢复到原始状态。 - undefined

0
您需要交互式地使用foo。我使用以下方法实现:
(global-set-key (kbd "C-M-r") (lambda () (interactive) ( foo)))

这应该可以解决你的问题。


抱歉,那不是解决方案。foo 不是一个函数而是一个按键映射表,因此 (foo) 没有意义。 - abo-abo
@abo-abo,我试图用(funcall (lambda-from-keymap 'foo))替换(foo),但是我收到了一个错误消息Symbol's function definition is void: lambda-from-keymap。顺便说一下,当我运行(funcall (lambda-from-keymap 'foo))时,我得到了相同的错误。 - S4M
1
“lambda-from-keymap”是我想要的虚构函数 :)。我希望有人可以向我展示如何实现它。 - abo-abo
@abo-abo 哦,我完全误解了你的问题!但现在我也想知道如何做! - S4M
1
这是一个接近解决方案的东西:http://oremacs.com/2015/02/02/colorful-hydrae/ - abo-abo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接