How do I bind a key to "the function represented by the following key sequence"? - emacs

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 }?

Sure there is, and it's the obvious way:
(global-set-key (kbd "<f1>") (kbd "C-u 2 C-x }"))

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.

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.

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

Related

Search-forward typo aborts macros

Search-forward seems to not work well inside emacs macros. For example, say I want to use a macro to help replace FOO with BAR in the following string:
aoeuFOOsnutehaFOOsanotehuFOO
I might begin recording a macro, search-forward for FOO, and then hit backspace a few times and type BAR. Then I can replay this macro to replace the rest of the occurrences. Pretty simple.
Suppose I hit the wrong key and search-forward for FOOO. I hit Backspace to remove the extra O, and finish recording the macro. But when I replay it, nothing happens. FOOO is not in the document, so the macro replay is immediately aborted when the search fails.
This gets annoying in longer macros. As it is, whenever I record a macro, I have to make sure I type in the search text for my search-forwards perfectly. If I make even one mistake, I have to cancel recording my macro and start over; otherwise, the macro will just abort when I replay it.
To sum up, if you use search-forward and commit a typo while recording a macro, the macro will not replay properly because it will abort as soon as it replays your typo.
Any workarounds or solutions to this problem?
You don't have to abort the macro: The easiest thing to do is to simply finish recording the macro (typos and all) and then edit it via C-x C-k C-e (kmacro-edit-macro-repeat) to remove the typo(s).
For instance, when you call this command after defining a macro that is supposed to simply search for occurrences of foo in the current buffer but contains a typo in the search (you typed fooo instead of foo before fixing the typo), the buffer for editing it would look like this:
C-s ;; isearch-forward
f ;; self-insert-command
ooo ;; self-insert-command * 3
DEL ;; delete-backward-char
RET ;; newline
To fix the macro, delete one of the os from the third line and remove the fourth line:
C-s ;; isearch-forward
f ;; self-insert-command
oo ;; self-insert-command * 2
RET ;; newline
Note that you don't have to change self-insert-command * 3 to self-insert-command * 2, I just did that to avoid confusion.
When you're done, hit C-c C-c to recompile the macro and close the *Edit macro* buffer.
The documentation of the ding function says (emphasis mine):
(ding &optional ARG)
Beep, or flash the screen.
Also, unless an argument is given, terminate any keyboard macro currently executing.
Digging into the source we can see that all isearch-* functions that call ding do so without passing a non-nil ARG. You could redefine these functions to change the way they call ding (not sure if it would be good idea to remove the calls to ding entirely), but there is an easier way to achieve what you want:
You can make sure ding is always called with a non-nil ARG by advising it as follows:
(defadvice ding (before be-nice activate compile)
(ad-set-arg 0 t))
As documented here, the ad-set-arg macro
sets the value of the actual argument at position to value.
So what the advice does is tell Emacs to set ARG to t before running the body of the original ding function, causing ding to be nice and not terminate keyboard macros anymore.
With the advice in place, you can now transform
aoeuFOOsnutehaFOOsanotehuFOO
into
aoeuBARsnutehaBARsanotehuBAR
with a "faulty" macro that contains a corrected typo:
C-s ;; isearch-forward
F ;; self-insert-command
OOO ;; self-insert-command * 3
DEL ;; delete-backward-char
RET ;; newline
3*DEL ;; delete-backward-char
BAR ;; self-insert-command * 3
EDIT
As #phils mentions in the comments below, allowing macro execution to continue regardless of errors can lead to unwanted consequences, so use this solution with care. If you want to be able to quickly enable or disable the advice so that you can use it selectively (i.e., in situations where you are sure it won't mess things up), define it like this:
(defadvice ding (before be-nice) ; advice not activated by default
(ad-set-arg 0 t))
and add a command for turning it on and off to your .emacs:
(defun toggle-ding-advice ()
(interactive)
(if (ad-is-active 'ding)
(ad-disable-advice 'ding 'before 'be-nice)
(ad-enable-advice 'ding 'before 'be-nice))
(ad-activate 'ding))
(global-set-key (kbd "C-c a") 'toggle-ding-advice)
You can then toggle the advice by simply pressing C-c a.

A quick way to repeatedly enter a variable name in Emacs?

I was just typing in this sort of code for Nth time:
menu.add_item(spamspamspam, "spamspamspam");
And I'm wondering if there's a faster way to do it.
I'd like a behavior similar to yasnippet's mirrors, except
I don't want to create a snippet: the argument order varies from
project to project and from language to language.
The only thing that's constant is the variable name that needs to be
repeated several times on the same line.
I'd like to type in
menu.add_item($,"")
and with the point between the quotes, call the shortcut and start typing,
and finally exit with C-e.
This seems advantageous to me, since there's zero extra cursor movement.
I have an idea of how to do this, but I'm wondering if it's already done,
or if something better/faster can be done.
UPD The yasnippet way after all.
Thanks to thisirs for the answer. This is indeed the yasnippet code I had initially in mind:
(defun yas-one-line ()
(interactive)
(insert "$")
(let ((snippet
(replace-regexp-in-string
"\\$" "$1"
(substring-no-properties
(delete-and-extract-region
(line-beginning-position)
(line-end-position))))))
(yas/expand-snippet snippet)))
But I'm still hoping to see something better/faster.
yasnippet can actually be used to create a snippet on-the-fly:
(defun yas-one-line ()
(interactive)
(let ((snippet (delete-and-extract-region
(line-beginning-position)
(line-end-position))))
(yas-expand-snippet snippet)))
Now just type:
menu.add_item($1,"$1")
and call yas-one-line. The above snippet is expanded by yasnippet!
You could try
(defvar sm-push-id-last nil)
(defun sm-push-id ()
(interactive)
(if (not sm-push-id-last)
(setq sm-push-id-last (point))
(text-clone-create sm-push-id-last sm-push-id-last
t "\\(?:\\sw\\|\\s_\\)*")
(setq sm-push-id-last nil)))
after which you can do M-x sm-push-id RET , SPC M-x sm-push-id RET toto and that will insert toto, toto. Obviously, this would make more sense if you bind sm-push-id to a convenient key-combo. Also this only works to insert a duplicate pair of identifiers. If you need to insert something else, you'll have to adjust the regexp. Using too lax a regexp means that the clones will tend to overgrow their intended use, so they may become annoying (e.g. you type foo") and not only foo but also ") gets mirrored on the previous copy).
Record a macro. Hit F3 (or possibly C-x (, it depends) to begin recording. Type whatever you want and run whatever commands you need, then hit F4 (or C-x )) to finish. Then hit F4 again the next time you want to run the macro. See chapter 17 of the Emacs manual for more information (C-h i opens the info browser, the Emacs manual is right at the top of the list).
So, for example, you could type the beginning of the line:
menu.add_item(spamspamspam
Then, with point at the end of that line, record this macro:
F3 C-SPC C-left M-w C-e , SPC " C-y " ) ; RET F4
This copies the last word on the line and pastes it back in, but inside of the quotes.

How can I capture the `M-0 <f4>` event and write it to a buffer in that format in Emacs?

I'm working on capturing formatted keystrokes to a buffer related to a VimGolf minor mode that I've been working on for a few days now.
I have the capture function almost exactly where I want it except that it cannot seem to capture the kmacro-end-or-call-macro command triggered by <f4> or the calc-dispatch command triggered by C-x *. I'm not sure what else is potentially missing as my testing has only exposed these anomalies.
In case you don't want to click over to github, here's the function as it stands now:
(defun vimgolf-capture-keystroke ()
(with-current-buffer (get-buffer-create vimgolf-keystrokes-buffer-name)
(end-of-buffer)
(if (not (or executing-kbd-macro prefix-arg))
(progn
(insert (key-description (this-command-keys)))
(insert " ")))))
(defun vimgolf-capture-keystrokes ()
(add-hook 'post-command-hook 'vimgolf-capture-keystroke))
(defun vimgolf-stop-capture-keystrokes ()
(remove-hook 'post-command-hook 'vimgolf-capture-keystroke))
The only connection I can see between the two problem functions that I know about is that they both recurse into executing other commands prior to finishing the command that they actually execute.
I can capture the beginning of the commands but not the end by transforming the capture functions above into:
(defun vimgolf-capture-keystrokes ()
(add-hook 'pre-command-hook 'vimgolf-capture-keystroke))
(defun vimgolf-stop-capture-keystrokes ()
(remove-hook 'pre-command-hook 'vimgolf-capture-keystroke))
Even when I turn off executing-kbd-macro from the restriction on appending to the buffer, it still does not capture that event. I tried to examine the actual vector that comes down from the post-command-hook and in all cases it's exactly what I would expect but for <f4> it renders an empty vector.
So how do I get those keys into my capture buffer?
For convenience, I've been testing this using Challenge ID 4d2fb20e63b08b08b0000075.
The exact keys I press are <f3> C-e M-4 M-b M-d C-b M-2 <C-S-backspace> M-2 C-y C-f <f4> M-0 <f4> C-o M-< C-x * : M-1 v r C-u y
The way those keys come into my capture buffer with the function as it stands right now is <f3> C-e M-4 M-b M-d C-b M-2 <C-S-backspace> M-2 C-y C-f <f4> C-o M-< : M-1 v r C-u y
I remembered a package that can do this -- I just tried it out and it captures those specific keys. The other nice thing, for your purposes, is that it limits the captured keys to the current buffer. Plus, you can specify certain commands that will not get logged, which would be handy for excluding the vimgolf-specific commands. (An aside; if you use a separate "control buffer" for vimgolf-mode, a la ediff-mode, then you won't get any vimgolf-specific keystrokes polluting the command log for the challenge buffer.)
The aforementioned package's limitation (currently) is that it won't capture keystrokes entered in the minibuffer (e.g. for M-x sort-lines RET), but that should be possible to extend by also capturing keys when (minibufferp).
The package works by hooking into pre-command-hook rather than the post-command-hook you are currently using.
FWIW, if you want you can do without the angle brackets in key descriptions -- they are never necessary (serve no purpose -- just noise). Library naked.el lets you do this. Description here.

Emacs copy with regex

I have a text file. Can Emacs select text based on regex and put it in kill-ring, so I can copy it somewhere else? Something like regex-kill-ring-save?
inspired by the already given comments (the Charles answer doesn't work as I would want it), I added a new function to the isearch/isearch-regexp mode map which puts only the matching string into the kill ring (whereas Charles proposal kills from current point to end of matching string):
(defun hack-isearch-kill ()
"Push current matching string into kill ring."
(interactive)
(kill-new (buffer-substring (point) isearch-other-end))
(isearch-done))
(define-key isearch-mode-map (kbd "M-w") 'hack-isearch-kill)
The nice thing about the isearch/isearch-regexp approach (which you can enable with C-s and C-M-s respectively) is that you can see your search string growing and you can copy it with M-w as soon as you are satisfied (and go back to where you have been before with C-u C-Space).
This works for me with Emacs 23.1. Don't know if it will work in all situations. Anyway I hope you find it useful :)
UPDATE: going through the emacswiki I stumbled over KillISearchMatch which suggests more or less the same (plus some more tips ...).
Cheers,
Daniel
I'm not sure if there is such a function already, but what you can do it with a keyboard macro:
Start recording a kbd macro: C-x (
Search for your regexp with search-forward-regexp
Move to the beginning of your match (the text you want to kill) with the various emacs navigation commands, e.g. search or backward-word etc.
Mark: C-spc
Move to the end of your match
Kill the text: C-w
You can then name the keyboard macro with M-x name-last-kbd-macro so that you can execute the macro with a name rather than with C-x e.
If you want to save the macro for future sessions, you can open your .emacs and insert the macro into the buffer with M-x insert-kbd-macro. After than you can bind a key to the macro just like you bind keys to normal emacs functions, e.g. (global-set-key "\C-c m" 'funky-macro-macro).
More about emacs keyboard macros
Isearch+ does this already. It optionally sets the region around the search target. You can use C-SPC C-SPC or M-= C-SPC at any time during Isearch to toggle this.
isearchp-deactivate-region-flag is a variable defined in isearch+.el.
Its value is t
Documentation:
Non-nil means isearching deactivates the region.
See also option isearchp-restrict-to-region-flag.
You can toggle this option using M-= C-SPC during Isearch.
You can customize this variable.

Convert Emacs macro into Elisp

Is there a way to convert an emacs macro into elisp, not like what M-x insert-kbd-macro does, the actual activity becoming elisp statements.
Thanks for your help.
Nope, sorry. There is no trivial way to convert an emacs macro into elisp.
Update: There's been some work on Emacs to start down this path. See this thread as a starting point. It's still not possible (June 2010), but there's activity.
The first reason I can think of is dealing with interactive commands and translating keystrokes into proper arguments for functions.
Think of the following sequence:
C-x b .em TAB RET
This begins the command to switch to a buffer, types three characters, uses TAB completion to complete it and RET to accept. The equivalent lisp for the end result (in an emacs session where the TAB completion is unique) is:
(switch-to-buffer ".emacs")
Thinking of completion, there are also interactions with expansion of all types (dabbrev, hippie-expand, etc.).
A starting point can be M-x edit-last-kbd-macro which (in my case) shows this:
;; Keyboard Macro Editor. Press C-c C-c to finish; press C-x k RET to cancel.
;; Original keys: C-x b .em <tab> RET
Command: last-kbd-macro
Key: none
Macro:
C-x b ;; switch-to-buffer
.em ;; self-insert-command * 3
<tab> ;; pabbrev-expand-maybe
RET ;; newline-and-indent
Which at least gives you some of the function names. But you'll see that RET is labeled as 'newline-and-indent which is incorrect because at the time of the macro execution, the minibuffer is active and the binding is in fact 'minibuffer-complete-and-exit. Similarly, the proper binding for TAB is 'minibuffer-complete.
I made a package that allows pretty much exactly this at https://github.com/Silex/elmacro
It has some quirks but it works pretty well... for example, the following macro:
F3 C-e M-b M-u C-a C-n F4
Generates the following elisp:
(defun upcase-last-word ()
"Change me!"
(interactive)
(move-end-of-line 1)
(backward-word 1)
(upcase-word 1)
(move-beginning-of-line 1)
(next-line 1 1))