Function: execute-kbd-macro

execute-kbd-macro is a function defined in macros.c.

Signature

(execute-kbd-macro MACRO &optional COUNT LOOPFUNC)

Documentation

Execute MACRO as a sequence of events.

If MACRO is a string or vector, then the events in it are executed exactly as if they had been input by the user.

If MACRO is a symbol, its function definition is used. If that is another symbol, this process repeats. Eventually the result should be a string or vector. If the result is not a symbol, string, or vector, an error is signaled.

COUNT is a repeat count, or nil for once, or 0 for infinite loop.

Optional third arg LOOPFUNC may be a function that is called prior to each iteration of the macro. Iteration stops if LOOPFUNC returns nil.

The buffer shown in the currently selected window will be made the current buffer before the macro is executed.

View in manual

Probably introduced at or before Emacs version 21.1.

Source Code

// Defined in /usr/src/emacs/src/macros.c
{
  Lisp_Object final;
  Lisp_Object tem;
  specpdl_ref pdlcount = SPECPDL_INDEX ();
  EMACS_INT repeat = 1;
  EMACS_INT success_count = 0;

  executing_kbd_macro_iterations = 0;

  if (!NILP (count))
    {
      count = Fprefix_numeric_value (count);
      repeat = XFIXNUM (count);
    }

  final = indirect_function (macro);
  if (!STRINGP (final) && !VECTORP (final))
    error ("Keyboard macros must be strings or vectors");

  tem = Fcons (Vexecuting_kbd_macro,
	       Fcons (make_int (executing_kbd_macro_index),
		      Vreal_this_command));
  record_unwind_protect (pop_kbd_macro, tem);

  /* The following loop starts the execution of possibly multiple
     iterations of the macro.

     The state variables that control the execution of a single
     iteration are Vexecuting_kbd_macro and executing_kbd_macro_index,
     which can be accessed from lisp. The purpose of the variables
     executing_kbd_macro and executing_kbd_macro_iteration is to
     remember the most recently started macro and its iteration count.
     This makes it possible to produce a meaningful message in case of
     errors during the execution of the macro.

     In a single iteration, individual characters from the macro are
     read by read_char, which takes care of incrementing
     executing_kbd_macro_index after each character.

     The end of a macro iteration is handled as follows:
      - read_key_sequence asks at_end_of_macro_p whether the end of the
        iteration has been reached.  If so, it returns the magic value 0
        to command_loop_1.
      - command_loop_1 returns Qnil to command_loop_2.
      - command_loop_2 returns Qnil to this function
        (but only the returning is relevant, not the actual value).

     Macro executions form a stack.  After the last iteration of the
     execution of one stack item, or in case of an error during one of
     the iterations, pop_kbd_macro (invoked via unwind-protect) will
     restore Vexecuting_kbd_macro and executing_kbd_macro_index, and
     run 'kbd-macro-termination-hook'.

     If read_char happens to be called at the end of a macro iteration,
     but before read_key_sequence could handle the end (e.g., when lisp
     code calls 'read-event', 'read-char', or 'read-char-exclusive'),
     read_char will simply continue reading other available input
     (Bug#68272).  Vexecuting_kbd_macro and executing_kbd_macro remain
     untouched until the end of the iteration is handled.

     This is similar (in observable behavior) to a possibly simpler
     implementation of keyboard macros in which this function pushed all
     characters of the macro into the incoming event queue and returned
     immediately.  Maybe this is the implementation that we ideally
     would like to have, but switching to it will require a larger code
     change.  */
  do
    {
      Vexecuting_kbd_macro = final;
      executing_kbd_macro = final;
      executing_kbd_macro_index = 0;

      kset_prefix_arg (current_kboard, Qnil);

      if (!NILP (loopfunc))
	{
	  Lisp_Object cont;
	  cont = call0 (loopfunc);
	  if (NILP (cont))
	    break;
	}

      command_loop_2 (list1 (Qminibuffer_quit));

      executing_kbd_macro_iterations = ++success_count;

      maybe_quit ();
    }
  while (--repeat
	 && (STRINGP (Vexecuting_kbd_macro) || VECTORP (Vexecuting_kbd_macro)));

  executing_kbd_macro = Qnil;

  Vreal_this_command = Vexecuting_kbd_macro;

  return unbind_to (pdlcount, Qnil);
}