How do I bind a key to "the function represented by the following key sequence"?
Asked Answered
L

4

9

I'm just starting to learn emacs (woohoo!) and I've been mucking around in my .emacs quite happily. Unfortunately, I don't know Lisp yet, so I'm having issues with the basics.

I've already remapped a few keys until I fix my muscle memory:

(global-set-key (kbd "<f9>") 'recompile)

That's fine. But how can I tell a key to 'simulate pressing several keys'? For instance, I don't know, make <f1> do the same as C-u 2 C-x } (widen buffer by two chars).

One way is to look up that C-x } calls shrink-window-horizontally, and do some sort of lambda thing. This is of course the neat and elegant way (how do you do this?). But surely there's a way to define <f1> to send the keystrokes C-u 2 C-x }?

Lacrosse answered 26/12, 2010 at 22:25 Comment(0)
D
9

For anything long-term, I would recommend the approach shown by seh, as that will naturally be more robust in most situations. It requires a little more work and know-how, of course, but it's all worthwhile :)

angus' approach is like a cut-down version of the keyboard macros feature that gives Emacs its name (and slightly simpler to use than macros for the example in question). You should definitely be aware of macros, however -- they can be exceedingly useful, and for anything more complicated it quickly becomes far easier to record one dynamically than to write out all the individual keys manually.

Here's the summary I wrote myself of the most important bits:

;;;; * Keyboard macros
;;   C-x (          or F3  Begin recording.
;;                     F3  Insert counter (if recording has already commenced).
;;   C-u <n> C-x (  or F3  Begin recording with an initial counter value <n>.
;;   C-x )          or F4  End recording.
;;   C-u <n> C-x )  or F4  End recording, then execute the macro <n>-1 times.
;;   C-x e          or F4  Execute the last recorded keyboard macro.
;;       e          or F4  Additional e or F4 presses repeat the macro.
;;   C-u <n> C-x e  or F4  Execute the last recorded keyboard macro <n> times.
;;   C-x C-k r             Apply the last macro to each line of the region.
;;   C-x C-k e             Edit a keyboard macro (RET for most recent).
;;   C-x C-k b             Set a key-binding.
;;
;; If you find yourself using lots of macros, you can even name them
;; for later use, and save them to your init file.
;;   M-x name-last-kbd-macro RET (name) RET
;;   M-x insert-kbd-macro RET (name) RET
;;
;; For more documentation:
;;   C-h k C-x (
;;   M-: (info "(emacs) Keyboard Macros") RET

If we play with the example from the question, you'll see how some of these things tie together...

To begin with, you can define the macro with F3C-u2C-x}F4

You could then bind it temporarily to F1 with C-xC-kbF1 (actually that's not true if F1 is currently a prefix key for an existing keymap, as typing it interactively will simply prompt for the remainder. You can circumvent this in code with (global-set-key (kbd "<f1>") ...), but I would suggest sticking to the reserved bindings).

If you then use describe-key (C-hk) to examine what is bound to that key, Emacs will show you a (lambda) expression which you could copy to your init file if you so wished.

Alternatively, you could name the macro and ask Emacs to insert the code into the current buffer:

M-x name-last-kbd-macro RET (name) RET
M-x insert-kbd-macro RETRET

This code will look different to the lambda expression shown by describe-key, but if you evaluate the inserted macro, you'll see the equivalence. You can likewise show that the (kbd "...") expression also evaluates to the same value, and therefore these are all just alternative ways of doing the same thing.

(You can use the *scratch* buffer to evaluate the code by moving point after the end of the expression, and either typing C-xC-e to show the value in the minibuffer, or C-j to insert the value into the buffer).

Note that the 'inserted' code uses fset to assign the macro to a symbol. You could bind the macro to a key either by executing the (fset) and then assigning that symbol to a key with (global-set-key), or you could ignore the (fset) and simply assign the macro value directly. This, of course, is directly equivalent to angus' answer.

Edit: I've just noticed that there's a kmacro-name-last-macro function bound to C-xC-kn which is nearly identical in form to name-last-kbd-macro, but which generates the lambda expression form seen when using kmacro-bind-to-key (C-xC-kb) and describe-key.

Disrespectful answered 27/12, 2010 at 5:12 Comment(1)
Many thanks! +1 for showing me the general way to do this sort of thing.Lacrosse
B
18

Sure there is, and it's the obvious way:

(global-set-key (kbd "<f1>") (kbd "C-u 2 C-x }"))
Bounteous answered 26/12, 2010 at 23:14 Comment(1)
Thank you so much. That is nowhere near as obvious as you think. :) Been searching for this for a long time.Bellona
D
9

For anything long-term, I would recommend the approach shown by seh, as that will naturally be more robust in most situations. It requires a little more work and know-how, of course, but it's all worthwhile :)

angus' approach is like a cut-down version of the keyboard macros feature that gives Emacs its name (and slightly simpler to use than macros for the example in question). You should definitely be aware of macros, however -- they can be exceedingly useful, and for anything more complicated it quickly becomes far easier to record one dynamically than to write out all the individual keys manually.

Here's the summary I wrote myself of the most important bits:

;;;; * Keyboard macros
;;   C-x (          or F3  Begin recording.
;;                     F3  Insert counter (if recording has already commenced).
;;   C-u <n> C-x (  or F3  Begin recording with an initial counter value <n>.
;;   C-x )          or F4  End recording.
;;   C-u <n> C-x )  or F4  End recording, then execute the macro <n>-1 times.
;;   C-x e          or F4  Execute the last recorded keyboard macro.
;;       e          or F4  Additional e or F4 presses repeat the macro.
;;   C-u <n> C-x e  or F4  Execute the last recorded keyboard macro <n> times.
;;   C-x C-k r             Apply the last macro to each line of the region.
;;   C-x C-k e             Edit a keyboard macro (RET for most recent).
;;   C-x C-k b             Set a key-binding.
;;
;; If you find yourself using lots of macros, you can even name them
;; for later use, and save them to your init file.
;;   M-x name-last-kbd-macro RET (name) RET
;;   M-x insert-kbd-macro RET (name) RET
;;
;; For more documentation:
;;   C-h k C-x (
;;   M-: (info "(emacs) Keyboard Macros") RET

If we play with the example from the question, you'll see how some of these things tie together...

To begin with, you can define the macro with F3C-u2C-x}F4

You could then bind it temporarily to F1 with C-xC-kbF1 (actually that's not true if F1 is currently a prefix key for an existing keymap, as typing it interactively will simply prompt for the remainder. You can circumvent this in code with (global-set-key (kbd "<f1>") ...), but I would suggest sticking to the reserved bindings).

If you then use describe-key (C-hk) to examine what is bound to that key, Emacs will show you a (lambda) expression which you could copy to your init file if you so wished.

Alternatively, you could name the macro and ask Emacs to insert the code into the current buffer:

M-x name-last-kbd-macro RET (name) RET
M-x insert-kbd-macro RETRET

This code will look different to the lambda expression shown by describe-key, but if you evaluate the inserted macro, you'll see the equivalence. You can likewise show that the (kbd "...") expression also evaluates to the same value, and therefore these are all just alternative ways of doing the same thing.

(You can use the *scratch* buffer to evaluate the code by moving point after the end of the expression, and either typing C-xC-e to show the value in the minibuffer, or C-j to insert the value into the buffer).

Note that the 'inserted' code uses fset to assign the macro to a symbol. You could bind the macro to a key either by executing the (fset) and then assigning that symbol to a key with (global-set-key), or you could ignore the (fset) and simply assign the macro value directly. This, of course, is directly equivalent to angus' answer.

Edit: I've just noticed that there's a kmacro-name-last-macro function bound to C-xC-kn which is nearly identical in form to name-last-kbd-macro, but which generates the lambda expression form seen when using kmacro-bind-to-key (C-xC-kb) and describe-key.

Disrespectful answered 27/12, 2010 at 5:12 Comment(1)
Many thanks! +1 for showing me the general way to do this sort of thing.Lacrosse
B
8

I'll use shrink-window-horizontally as the example function, but you can generalize the idea to any bindings you'd like to define.

If you want to use two as the default amount to shrink the window, rather than one, try the following:

(global-set-key [f9]
  (lambda (&optional n)
    (interactive "P")
    (shrink-window-horizontally (or n 2))))

That binds the F9 key to an interactive function accepting a prefix argument. If you just press F9, you'll pass no argument, which summons the default value of 2, as the parameter n will receive nil as an argument. However, if you press, say, C-u 10 F9, you'll pass ten as the argument for n. This allows you to use your binding more flexibly.

Bluebell answered 26/12, 2010 at 23:49 Comment(0)
E
0

general-simulate-key from general.el works better (in my case a sequence with popups and changing keymaps that I couldn't get to work with macros): https://github.com/noctuid/general.el#simulating-keypresses

East answered 26/3, 2018 at 21:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.