3

I have used keyboard macros for a long time, but moved between companies, systems, and various machines over my career. I used to be able to record macros and insert-kbd-macro into my .emacs file and they look readable and concise:

(fset 'kc-del-blank-lines
      "M-x flush-lines return ^\s-*$ return")

On my latest, cygwin install (windows 10), the same macro gets created like:

(fset 'kc-rmbl
   [?\M-x ?f ?l ?u ?s ?h tab return ?^ ?\\ ?s ?- ?* ?$ return])

Ok, the latter uses a vector ([...]) instead of a string ("..."). How can I get this install to produce the former results.

1
+100

insert-kbd-macro can insert a number of different forms, but ultimately the definition of the macro can be either a string, a vector, or a lambda. If the macro definition is a string, then it outputs the form you are used to seeing. If it's a vector, then it outputs the form you now get. If the macro definition is a lambda, then the body of the macro will be printed as a call to kmacro-lambda-form which builds the same lambda.

The last change to the name-last-kbd-macro was commit 4f779ff8 in 2017, when it was removed and made an alias of kmacro-name-last-macro. kmacro-name-last-macro has since 2004 always created a definition which is a lambda containing a vector, like this:

(lambda
  (&optional arg)
  "Keyboard macro."
  (interactive "p")
  (kmacro-exec-ring-item
   '([19 63 return 102 111 111 5 right]
     0 "%d")
   arg))

(This is the lambda form that insert-kbd-macro checks for).

Prior to this change name-last-kbd-macro simply fset your choosen name with the value of last-kbd-macro. last-kbd-macro has been a vector of keys since at least 1995. However, in 1999 in commit f8a4db7d code was added to start-kdb-macro that checked to see if the current value of last-kbd-macro was a string. The comment says "Check the type of last-kbd-macro in case Lisp code changed it.", so perhaps there was some lisp code running around which replaced the value of last-kbd-macro with a string instead of a vector. However, this particular code is only used when appending new keystrokes to the end of the most recently-recorded macro, and it converts the string back into a vector.

There's just one last place to look: end_kbd_macro sets last-kbd-macro to the newly-defined macro using a function make_event_array. This function creates either a string or a vector. It only creates a string if all the characters are less than 128 after masking off the CHAR_META bit "and all the bits above it. It's done this since the code was introduced in commit 7146af97 in 1991, so I suppose the comments about checking the type "in case Lisp code changed it" was somewhat mistaken. Here's the code from alloc.c, line 3672:

for (i = 0; i < nargs; i++)
   /* The things that fit in a string
      are characters that are in 0...127,
      after discarding the meta bit and all the bits above it.  */
   if (!FIXNUMP (args[i])
   || (XFIXNUM (args[i]) & ~(-CHAR_META)) >= 0200)
     return Fvector (nargs, args);

However, I notice that CHAR_META is the highest bit; all the other modifier bits have lower values than CHAR_META. CHAR_META has had the highest value since the constant was introduced in 1993, but prior to that it's really hard to tell. The check against CHAR_META in make_event_array was also added in 1993 (in commit 736471d1); prior to that it accepted any character less than 256.

So, I conclude that you either had a really old version of Emacs, or that your memory is faulty, or that there's something else going on (such as modifications to the OSX port of Emacs that you used to use). Emacs has for at least the last 27 years output virtually all keyboard macros as vectors, unless they contained only ascii characters with no modifier keys, and only if the macro had not been named with kmacro-name-last-macro (because then it would output the form evaluating to a lambda).

You could try patching make_event_array to use CHAR_ALT instead of CHAR_META, so that it ignores all modifier keys, and see if that improves the situation at all. I'd try it myself, but my emacs build is slightly broken at the moment.

7
  • That looks like some good digging! It's definitely the case that insert-kbd-macro used to insert different things depending on which of the "name last macro" variants you'd used. I hadn't realised that one of those commands had been aliased to the other since I'd noted the behaviour, though, so I was slightly confused when I couldn't replicate the behaviour when I saw this question.
    – phils
    Dec 21 '20 at 5:48
  • I've located a macro in my config in which I've included (in comments) both forms. This is possibly dating to when I noticed the behaviour in question. One begins (fset 'my-erc-window-config "\C-x1\C-xb*Sauron*\C-m\C-[xtoggle-window-dedicated\C-m... and the other one begins (fset 'my-erc-window-config [?\C-x ?1 ?\C-x ?b ?* ?S ?a ?u ?r ?o ?n ?* ?\C-m ?\M-x ?t ?o ?g ?g ?l ?e ?- ?w ?i ?n ?d ?o ?w ?- ?d ?e ?d ?i ?c ?a ?t ?e ?d ?\C-m ...
    – phils
    Dec 21 '20 at 5:53
  • n.b. I can't date that code precisely (it's in an untracked file of local settings), but that old-style macro string was definitely generated within the past decade. I don't believe it would have been anything older than Emacs 23.3.
    – phils
    Dec 21 '20 at 6:11
  • @phils: What platform(s) do you use?
    – db48x
    Dec 21 '20 at 6:26
  • GNU/Linux (Ubuntu).
    – phils
    Dec 21 '20 at 7:14

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.