2

There's a phenomenally useful feature of emacs lisp where you can evaluate the result of an expression and paste the result directly into a buffer.

Say I want to check addition works. Then I type:

(* 3 2)

and I define the keyboard macro:

(setq last-kbd-macro
   [down ?\( ?i ?s ?= ?  ?\C-\M-f ?  ?\C-u ?\C-x ?\C-e ?\) home])

If I then place point above the expression, and press F4 to execute the macro, the expression turns into:

(is= (* 3 2) 6)

Which makes a nice regression test.

Unfortunately the same keyboard macro executed in a clojure/nrepl buffer results in:

(* 8 9)(is=  )

and an error from clojure about not being able to resolve the symbol is=

So I think that something weird is happening to the ordering of things, and the macro is trying to evaluate the wrong thing.

Can anyone get this to work with clojure? (And in fact solve the general problem so that arbitrary keyboard macros work OK with C-u C-x C-e like they do with emacs lisp)


Edit since people seem to be misunderstanding:

Doing the keypresses by hand works fine in either an elisp or a clojure buffer. In one C-u C-x C-e evals with emacs lisp and in the other evals in the external clojure process.

The problem comes when trying to run a keyboard macro (recorded in a clojure buffer) which contains C-u C-x C-e

Running the macro in the clojure buffer, things get re-ordered somehow. It looks like the macro may be carrying on executing even though the eval-paste has not completed yet.

I was wondering if there was a way of forcing the keyboard macro (or corresponding function) to execute in the same order as it would by hand.

I'm quite happy to turn the keyboard macro into a proper elisp function if necessary.

John Lawrence Aspden
  • 17,124
  • 11
  • 67
  • 110
  • C-xC-e is evals the form against an internal lisp prcocess (that is, clojure) and not Emacs lisp. Hence the error. – Phil Lord Feb 19 '13 at 15:29
  • Phil, it depends what you mean by 'hence'. It looks as though the macro carries on running while the eval-paste is still executing. Is there a way to wait for it to complete rather than (apparently) forking a separate thread? – John Lawrence Aspden Feb 19 '13 at 19:23

2 Answers2

1

Keyboard macros are a quick and dirty way to repeat a certain sequence of actions. They are very fragile because they remember the keys pressed, not the functions they invoke, so they may produce wildly different results depending on the buffer they are invoked in, the current command history, window configuration and what not.

In your case, chances are that one of the keys in the macro invoke a different command in the clojure/nrepl buffer than in the buffer in which you tested the macro. You really need to define the macro in the buffer in which it will be used.

If you are going to re-use the macro, I suggest that you write a emacs lisp function which does what you want instead of messing with macros.

You might find the output of (format-kbd-macro nil t) useful, but note that you should not use the commands like eval-last-sexp in your function, use a lower-level function which returns the evaluation result instead of inserting it into the current buffer.

sds
  • 58,617
  • 29
  • 161
  • 278
  • Thanks sds, but that's not the problem. The same keystrokes work fine by hand. I think it's something to do with having short pauses between them when doing it 'by hand'. – John Lawrence Aspden Feb 19 '13 at 19:25
  • Don't use macro. Write a function which evaluates the code in clojure and inserts the results. This will solve your timing problem. – sds Feb 19 '13 at 19:34
  • (defun my-eval-paste () (interactive) (next-line) (insert "(is= ") (forward-sexp) (insert " ") (eval-last-sexp 't) (insert ")") ) doesn't quite work in elisp. I end up with (is= (* 8 9) 72 (#o110, #x48)) Can you see where the (#o110, #x48) is coming from? – John Lawrence Aspden Feb 19 '13 at 19:50
  • The clojure version (defun my-eval-paste () (interactive) (next-line) (insert "(is= ") (forward-sexp) (insert " ") (nrepl-eval-last-expression 't) (insert ")") ) almost works, but I end up with: (is= (* 8 9) )72 , as if the insert ")" was executed before the paste happened. – John Lawrence Aspden Feb 19 '13 at 19:54
  • don't use `nrepl-eval-last-expression` and `eval-last-sexp`; use a lower-level function which returns a string instead of inserting it. – sds Feb 19 '13 at 19:56
  • Ah, brilliant, will do! (I also just found (sleep-for 0.1), which does the trick, but your way is better). Thanks very much. – John Lawrence Aspden Feb 19 '13 at 20:01
1

Addressing a problem you haven't had yet, but will soon: don't forget to insert a ' character before the result of evaluating the expression: you want (= (cons 1 nil) '(1)), not (= (cons 1 nil) (1)).

amalloy
  • 89,153
  • 8
  • 140
  • 205