unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
From: Juri Linkov <juri@jurta.org>
To: emacs-devel@gnu.org
Subject: Convert a keyboard macro to equivalent Lisp code
Date: Sat, 05 Jun 2010 22:57:04 +0300	[thread overview]
Message-ID: <87fx11mfd3.fsf@mail.jurta.org> (raw)

There is no progress in implementing the task of conversion
a keyboard macro to equivalent Lisp code because we approached
this task with the "all-or-nothing" attitude.  We tried to find
the most difficult and controversial cases to justify not doing
even basic and straightforward things.

I propose to install the basic support for recording all commands
and their arguments that will allow everyone to experiment with
post-processing to convert the plain trace of executed Lisp commands
into something more useful.  (It's mainly the function
`kmacro-convert-command-history' that could be modified to produce
better results).

=== modified file 'lisp/macros.el'
--- lisp/macros.el	2010-03-03 17:31:50 +0000
+++ lisp/macros.el	2010-06-05 19:32:24 +0000
@@ -280,6 +280,41 @@
 
 ;;;###autoload (define-key ctl-x-map "q" 'kbd-macro-query)
 
+;;; Convert a keyboard macro to Lisp code.
+
+;;;###autoload
+(defun insert-last-kbd-macro-command-history ()
+  (interactive)
+  (insert
+   "(defun last-kbd-macro-command-history ()\n"
+   "  \"Command created from the last keyboard macro.\"\n")
+  (comment-region (prog1 (point)
+		    (insert "  Original keys: ")
+		    (insert-kbd-macro (intern "")))
+		  (point))
+  (insert "  (interactive)\n")
+  (dolist (s (kmacro-convert-command-history
+	      last-kbd-macro-command-history))
+    (insert "  " (pp-to-string s)))
+  (insert ")\n"))
+
+(defun kmacro-convert-command-history (commands)
+  (let ((cmds commands) cmd name ret)
+    (while cmds
+      (setq cmd (car cmds))
+      (setq name (car cmd))
+      (cond
+       ;; Skip next commands.
+       ((memq name '(
+		     kmacro-start-macro kmacro-end-macro
+		     universal-argument universal-argument-other-key
+		     digit-argument
+		     execute-extended-command
+		     )))
+       (t (push cmd ret)))
+      (setq cmds (cdr cmds)))
+    (nreverse ret)))
+
 (provide 'macros)
 
 ;; arch-tag: 346ed1a5-1220-4bc8-b533-961ee704361f

=== modified file 'src/callint.c'
--- src/callint.c	2010-03-23 16:25:12 +0000
+++ src/callint.c	2010-06-05 19:45:36 +0000
@@ -43,6 +43,7 @@
 
 extern Lisp_Object Vhistory_length;
 extern Lisp_Object Vthis_original_command, real_this_command;
+extern Lisp_Object Vthis_command_args;
 extern int history_delete_duplicates;
 
 Lisp_Object Vcommand_debug_status, Qcommand_debug_status;
@@ -326,7 +327,7 @@
 
   specs = Qnil;
   string = 0;
-  /* The idea of FILTER_SPECS is to provide away to
+  /* The idea of FILTER_SPECS is to provide a way to
      specify how to represent the arguments in command history.
      The feature is not fully implemented.  */
   filter_specs = Qnil;
@@ -357,23 +358,24 @@
     }
   else
     {
-      Lisp_Object input;
+      Lisp_Object input, values;
       i = num_input_events;
       input = specs;
       /* Compute the arg values using the user's expression.  */
       GCPRO2 (input, filter_specs);
       specs = Feval (specs);
       UNGCPRO;
+
+      /* Make a copy of the list of values, for the command history,
+	 and turn them into things we can eval.  */
+      values = quotify_args (Fcopy_sequence (specs));
+      fix_command (input, values);
+      Vthis_command_args = values;
+
       if (i != num_input_events || !NILP (record_flag))
 	{
 	  /* We should record this command on the command history.  */
-	  Lisp_Object values;
-	  Lisp_Object this_cmd;
-	  /* Make a copy of the list of values, for the command history,
-	     and turn them into things we can eval.  */
-	  values = quotify_args (Fcopy_sequence (specs));
-	  fix_command (input, values);
-	  this_cmd = Fcons (function, values);
+	  Lisp_Object this_cmd = Fcons (function, values);
 	  if (history_delete_duplicates)
 	    Vcommand_history = Fdelete (this_cmd, Vcommand_history);
 	  Vcommand_history = Fcons (this_cmd, Vcommand_history);
@@ -387,6 +389,15 @@
 	    }
 	}
 
+      if (!NILP (current_kboard->defining_kbd_macro)
+	  && ! (minibuf_level > 0))
+	{
+	  /* We should record this command on the macro command history.  */
+	  current_kboard->Vlast_kbd_macro_command_history
+	    = Fcons (Fcons (function, values),
+		     current_kboard->Vlast_kbd_macro_command_history);
+	}
+
       Vthis_command = save_this_command;
       Vthis_original_command = save_this_original_command;
       real_this_command= save_real_this_command;
@@ -831,18 +842,24 @@
 
   args[0] = function;
 
+  /* Make a list of values, for the command history.  */
+  visargs[0] = function;
+  for (i = 1; i < count + 1; i++)
+    {
+      if (varies[i] > 0)
+	visargs[i] = Fcons (intern (callint_argfuns[varies[i]]), Qnil);
+      else
+	visargs[i] = quotify_arg (args[i]);
+    }
+  Vthis_command_args = Fcdr (Flist (count + 1, visargs));
+
   if (arg_from_tty || !NILP (record_flag))
     {
-      visargs[0] = function;
-      for (i = 1; i < count + 1; i++)
-	{
-	  if (varies[i] > 0)
-	    visargs[i] = Fcons (intern (callint_argfuns[varies[i]]), Qnil);
-	  else
-	    visargs[i] = quotify_arg (args[i]);
-	}
-      Vcommand_history = Fcons (Flist (count + 1, visargs),
-				Vcommand_history);
+      /* We should record this command on the command history.  */
+      Lisp_Object this_cmd = Fcons (function, Vthis_command_args);
+      if (history_delete_duplicates)
+	Vcommand_history = Fdelete (this_cmd, Vcommand_history);
+      Vcommand_history = Fcons (this_cmd, Vcommand_history);
       /* Don't keep command history around forever.  */
       if (INTEGERP (Vhistory_length) && XINT (Vhistory_length) > 0)
 	{
@@ -852,6 +869,14 @@
 	}
     }
 
+  if (!NILP (current_kboard->defining_kbd_macro)
+      && ! (minibuf_level > 0))
+    {
+      current_kboard->Vlast_kbd_macro_command_history
+	= Fcons (Fcons (function, Vthis_command_args),
+		 current_kboard->Vlast_kbd_macro_command_history);
+    }
+
   /* If we used a marker to hold point, mark, or an end of the region,
      temporarily, convert it to an integer now.  */
   for (i = 1; i <= count; i++)

=== modified file 'src/keyboard.c'
--- src/keyboard.c	2010-05-25 09:36:21 +0000
+++ src/keyboard.c	2010-06-05 19:33:02 +0000
@@ -339,6 +339,9 @@
    command is stored in this-original-command.  It is nil otherwise.  */
 Lisp_Object Vthis_original_command;
 
+/* Arguments of the command being executed by the command loop.  */
+Lisp_Object Vthis_command_args;
+
 /* The value of point when the last command was started.  */
 int last_point_position;
 
@@ -1630,6 +1633,7 @@
       Vthis_command = Qnil;
       real_this_command = Qnil;
       Vthis_original_command = Qnil;
+      Vthis_command_args = Qnil;
       Vthis_command_keys_shift_translated = Qnil;
 
       /* Read next key sequence; i gets its length.  */
@@ -11471,6 +11475,7 @@
   kb->kbd_macro_bufsize = 0;
   kb->defining_kbd_macro = Qnil;
   kb->Vlast_kbd_macro = Qnil;
+  kb->Vlast_kbd_macro_command_history = Qnil;
   kb->reference_count = 0;
   kb->Vsystem_key_alist = Qnil;
   kb->system_key_syms = Qnil;
@@ -11990,6 +11995,10 @@
 result of looking up the original command in the active keymaps.  */);
   Vthis_original_command = Qnil;
 
+ DEFVAR_LISP ("this-command-args", &Vthis_command_args,
+       doc: /* Arguments of the command being executed.  */);
+ Vthis_command_args = Qnil;
+
   DEFVAR_INT ("auto-save-interval", &auto_save_interval,
 	      doc: /* *Number of input events between auto-saves.
 Zero means disable autosaving due to number of characters typed.  */);
@@ -12477,6 +12486,7 @@
       mark_object (kb->kbd_queue);
       mark_object (kb->defining_kbd_macro);
       mark_object (kb->Vlast_kbd_macro);
+      mark_object (kb->Vlast_kbd_macro_command_history);
       mark_object (kb->Vsystem_key_alist);
       mark_object (kb->system_key_syms);
       mark_object (kb->Vwindow_system);

=== modified file 'src/keyboard.h'
--- src/keyboard.h	2010-01-13 08:35:10 +0000
+++ src/keyboard.h	2010-06-05 19:32:07 +0000
@@ -122,6 +122,9 @@
     /* Last anonymous kbd macro defined.  */
     Lisp_Object Vlast_kbd_macro;
 
+    /* Command history of last anonymous kbd macro defined.  */
+    Lisp_Object Vlast_kbd_macro_command_history;
+
     /* Alist of system-specific X windows key symbols.  */
     Lisp_Object Vsystem_key_alist;
 

=== modified file 'src/macros.c'
--- src/macros.c	2010-01-13 08:35:10 +0000
+++ src/macros.c	2010-06-05 19:35:19 +0000
@@ -133,6 +133,7 @@
       message ("Appending to kbd macro...");
     }
   current_kboard->defining_kbd_macro = Qt;
+  current_kboard->Vlast_kbd_macro_command_history = Qnil;
 
   return Qnil;
 }
@@ -148,6 +149,8 @@
     = make_event_array ((current_kboard->kbd_macro_end
 			 - current_kboard->kbd_macro_buffer),
 			current_kboard->kbd_macro_buffer);
+  current_kboard->Vlast_kbd_macro_command_history
+    = Fnreverse (current_kboard->Vlast_kbd_macro_command_history);
 }
 
 DEFUN ("end-kbd-macro", Fend_kbd_macro, Send_kbd_macro, 0, 2, "p",
@@ -402,6 +405,9 @@
 
   DEFVAR_KBOARD ("last-kbd-macro", Vlast_kbd_macro,
 		 doc: /* Last kbd macro defined, as a string or vector; nil if none defined.  */);
+
+  DEFVAR_KBOARD ("last-kbd-macro-command-history", Vlast_kbd_macro_command_history,
+		 doc: /* Command history of last kbd macro defined, as a list; nil if none defined.  */);
 }
 
 /* arch-tag: d293fcc9-2266-4163-9198-7fa0de12ec9e

-- 
Juri Linkov
http://www.jurta.org/emacs/



             reply	other threads:[~2010-06-05 19:57 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2010-06-05 19:57 Juri Linkov [this message]
2010-06-07  9:27 ` Convert a keyboard macro to equivalent Lisp code Richard Stallman
2010-06-07  9:27 ` Richard Stallman
2010-06-07 13:36   ` Stefan Monnier
2010-06-07 18:33   ` Juri Linkov
2010-06-07 18:35 ` Juri Linkov
2010-06-07 20:11   ` Stefan Monnier
2010-06-08  8:05     ` Juri Linkov
2010-06-09  1:36       ` Stefan Monnier
2010-06-09  8:34         ` Juri Linkov
2010-06-09 14:17           ` Stefan Monnier

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87fx11mfd3.fsf@mail.jurta.org \
    --to=juri@jurta.org \
    --cc=emacs-devel@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).