Search code examples
emacselispkey-bindings

how to emulate a specific key press in Emacs Lisp


Context: I want to make a minor mode where pressing f twice fast results in whatever the pressing of ( should do at that time. This doesn't always mean just insertion of (. For example, in buffers where paredit mode or autopair mode is enabled, pressing of ( usually results in insertion of (). In a paredit mode buffer, that sometimes results in wrapping the selected text: for example, if I select a b and press (, that should result in replacing the selection with (a b).

For detection of f being pressed twice, I just need to take the logic in the short code in http://www.emacswiki.org/emacs/electric-dot-and-dash.el

So the only missing piece is a Lisp code snippet that tells Emacs "Trigger pressing of ( now!"

The first thing that came to my mind was that the snippet should do

  1. find the command bound to the key (
  2. and then call call-interactively on that command.

but that breaks down if the auto pairing package (autopair or paredit or other similar package) binds ( to a command that has a logic that looks up what key was used to call the command, or if the package simply relies on post-self-insert-hook or post-command-hook instead of binding (.


update

I've looked up Key Chord documentation and it turns out what I am trying to do with answers to this question has a simpler solution:

(require 'key-chord)
(key-chord-mode 1)

(defvar my-easy-open-paren-mode-map
  (let ((map (make-sparse-keymap)))
    (key-chord-define map ",." (kbd "("))
    map))
(define-minor-mode my-easy-open-paren-mode
  "In this mode, pressing . and , together is another way of pressing the open paren.")

(defvar my-easy-semicolon-mode-map
  (let ((map (make-sparse-keymap)))
    (key-chord-define map ";;" (kbd "C-e ;"))
    map))
(define-minor-mode my-easy-semicolon-mode
  "In this mode, pressing semicolon twice fast is another way of pressing C-e and semicolon.")

(add-hook 'prog-mode-hook 'my-easy-open-paren-mode)

(add-hook 'c-mode-common-hook 'my-easy-semicolon-mode)

Triggering key press may still be useful in other contexts though.


Solution

  • You might appreciate the Key Chord library for binding functions to a double key-press. (I wouldn't recommend using f if you'll be writing in English, mind you; but YMMV.)

    post-self-insert-hook would still run if the binding was self-insert-command. post-command-hook will run in any case, but if you're worried about it seeing an incorrect function and/or input event, you can manipulate those...

    After looking up the binding, your function can set this-command to the function you're about to call-interactively, and last-command-event to the required key. e.g.:

    (defun my-fake-paren ()
      (interactive)
      (let ((command (key-binding "(")))
        (setq last-command-event ?\()
        (setq this-command command)
        (call-interactively command)))