8

Given a string in elisp, I'd like to simulate emacs receiving whatever keypresses are necessary to re-create that string. So given the string "Hello" emacs would act exactly the same as if I had pressed Shift+H,e,l,l,o. Note that this is not always the same thing as inserting text into the buffer -- some modes like isearch-mode listen to keypress events directly.

The reason this is necessary is because there are lots of modes and extensions that make the assumption that all input comes from the keyboard or make the assumption that any buffer/window/frame change will be associated with the keyboard input event, and a lot of the time they inspect data associated with that event. For the speech recognition mode that I work on to work seamlessly with the existing body of code, it can't just call directly into elisp to perform actions, instead it needs to tell elisp to press keys to cause those actions to happen.

I have found unread-command-events, but I'm not aware of any existing code that will translate strings to the necessary key sequences. Does something like that already exist?

5

Does

(execute-kbd-macro (kbd "Hello"))

work by any chance?

  • 2
    It'll work in this case, but not with the string "C-Beams" for example. One way to avoid it is to add spaces between each character. (let ((string "C-Beams")) (execute-kbd-macro (kbd (mapconcat #'char-to-string string " ")))) will work. Probably there's a better way... – YoungFrog Jun 3 '15 at 17:40
  • Hmm, the mapconcat trick doesn't work if the string actually contains spaces, e.g. "C beams" becomes "Cbeams". (execute-kbd-macro (kbd "SPC")) works though, so you could write something a bit more elaborate than mapconcat to do that substitution... this makes me worry though there may be other relevant keys though that need this special treatment. – Joseph Garvin Jun 4 '15 at 16:40
  • Looks like newlines and tabs also have this issue. I can deal with substituting SPC/RET/TAB, although I wonder if this whole approach is going to have problems with international keyboards. – Joseph Garvin Jun 4 '15 at 17:07
  • (mapc (lambda (char) (execute-kbd-macro (char-to-string char))) string) seems to work ok. But (of course?) if there's a tab character, it'll pretend it is hitting TAB, which might not be bound to a command inserting an actual tab... – YoungFrog Jun 4 '15 at 20:52
  • @YoungFrog: that appears to still be broken by \n – Joseph Garvin Jun 8 '15 at 14:15
4

Okay, so my first answer has a number of shortcomings as detailed in its comments.

execute-kbd-macro is a built-in function in C source code.

(execute-kbd-macro MACRO &optional COUNT LOOPFUNC)

Execute MACRO as string of editor command characters. MACRO can also be a vector of keyboard events. If MACRO is a symbol, its function definition is used. COUNT is a repeat count, or nil for once, or 0 for infinite loop.

Optional third arg LOOPFUNC may be a function that is called prior to each iteration of the macro. Iteration stops if LOOPFUNC returns nil.

(emphasis mine)

So a solution to pass a string straight through to Emacs as individual keypresses is:

(defun insert-via-kbd (str)
  (execute-kbd-macro (vconcat str)))

This seems to work quite well (it even handles Japanese!), but there are still some oddities:

  • \n will trigger C-j (not C-m, RET, or <return>)
  • \t will trigger TAB (not <tab>)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.