1

Sometimes I run into a problem with Emacs that goes away if invoke Emacs with the --no-init-flag.

In other word, I can deduce that the problem in question is caused by code somewhere in my .emacs file.

The only way I know to pinpoint what exactly in my (very long and very old) .emacs file is causing the problem is what could be called "debugging by bisection". For those who don't know what I mean by this, I give a more detailed description of this procedure below. For the purpose of this question, however, what matters is that the method entails multiple rounds of

  1. CREATE a (trial/experimental) init file for Emacs;
  2. START Emacs with the init file from (1);
  3. TEST whether the problem occurs or not;
  4. KILL Emacs;
  5. REPEAT.

The speed and convenience of this procedure depends on how quickly and conveniently I can carry out the TEST step1.

Very often it happens that the only way I know to carry out this TEST step is to go through a sequence of interactive operations (e.g. open a new buffer, enable this or that mode mode, issue this or that command, etc.)

My question is this: how can I record in a file /path/to/test/sequence the sequence of interactive operations that make up the TEST step, so that I can replay this sequence automatically by including the expression

(load "/path/to/test-sequence")

at the right place in my init file?

Also, could this recorded sequence of operation also include the one to KILL Emacs (step 4)?


1 The other steps in the cycle are usually relatively simple and largely independent of the problem I'm debugging. The TEST step, in contrast, depends entirely on the problem being debugged, and can therefore vary greatly in degree of difficulty.


DEBUGGING MY .emacs FILE "BY BISECTION"

If somehow I already know that the problem I'm debugging is caused by code that lies after the first a_i lines, but before the first b_i + 1 lines of my standard .emacs file, I can start Emacs with an init file consisting of c_i lines of my standard .emacs file, where c_i is approximately equal to (a_i + b_i)/2, and check whether the problem occurs. If it does occur, I then will know that the problem is caused by code that lies after the first a_i lines, but before the first c_i + 1 lines of my standard .emacs file. If it doesn't occur, I then will know that the problem is caused by code that lies after the first c_i lines, but before the first b_i + 1 lines of my standard .emacs file. In the first case, I set a_{i+1} = a_i and b_{i+1} = c_i; in the second, I set a_{i+1} = c_i and b_{i+1} = b_i. Now I can apply the same procedure all over again. At each iteration, the difference b_i - a_i shrinks, therefore eventually this difference will become small enough for me to be able to identify the "top-level" S-expression that is causing the problem.

Since I already know that my standard .emacs does cause the problem, I can begin the induction by setting a_0 = 0 and b_0 = N (where is the number of lines in my standard .emacs file), and then carrying on as described above. I.e. I start Emacs with an init file consisting of approximately the first (a_0 + b_0)/2 = (0 + N)/2 = N/2 lines of my standard .emacs file, and test for the occurrence of the problem. Based on the outcome of this test, I establish the values of a_1 and b_1, and repeat the process. Etc.

2

If was me, I'd just use a keyboard macro. Hopefully your test procedure doesn't abort recording the macro. If so, you'll have to find some way to wrap that last command.

  1. Start the macro recording with C-x (

  2. ;; do your interactive stuff

  3. End recording the macro with C-x )

  4. Name the macro something meaningful (eg. "mykmacro") with C-x C-k n mykmacro RET

  5. Save your macro

    • visit a new file with C-x C-f mykmacro.el
    • insert your named macro into the buffer with M-x insert-kbd-macro
    • save the file C-x C-s
    • exit Emacs C-x C-x

Now, in your shell you can run your macro non-interactively against your init file.

$ cat myinit.el
(message "this msg is from myinit.el")

$ cat mykmacro.el
;; this kbd macro inserts "foo" on two lines, and capitalizes the first

(fset 'mykmacro
   (kmacro-lambda-form [?\C-a ?f ?o ?o return ?f ?o ?o return up up ?\C-a ?\M-x ?c ?a ?p ?i ?t ?a ?l ?i ?z ?e ?- ?w ?o ?r tab return down down ?\C-a] 0 "%d"))

$ cat /tmp/foobar.txt
cat: /tmp/foobar.txt: No such file or directory

$ emacs --batch -l myinit.el -l mykmacro.el --eval '(find-file "/tmp/foobar.txt")' -f mykmacro -f save-buffer
this msg is from myinit.el

$ cat /tmp/foobar.txt
Foo
foo

Obviously the meat here is in the invocation of emacs. The argument break down is like this.

  • --batch: use non-interactive "batch" mode
  • -l myinit.el: batch implies -q so we must manually load our init file
  • -l mykmacro.el: load the file where we saved our keyboard macro
  • --eval ...: eval a lisp form (open /tmp/foobar.txt)
  • -f mymkmacro: call a lisp function without arguments. a shortcut for --eval '(mykmacro)'
  • -f save-buffer: again, but with save-buffer (ie. C-x C-s)

Obviously this is a bit of a contrived example for demonstration purposes. Since you're testing your init file all you'd probably need is something like

$ emacs --batch -l your_test_init.el -l your_kbd_macro.el -f your_kbd_macro

Hopefully, this gets you squared away. If not, Emacs comes with a test rig for unit testing called, ert, that you may want to look into.

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.