all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
From: Juri Linkov <juri@jurta.org>
To: Stefan Monnier <monnier@iro.umontreal.ca>
Cc: yzhh <yezonghui@gmail.com>, emacs-devel@gnu.org
Subject: Re: Is there a plan to record kbd macro as elisp code?
Date: Sun, 28 Oct 2007 00:45:45 +0300	[thread overview]
Message-ID: <87y7doxmcw.fsf@jurta.org> (raw)
In-Reply-To: <jwv8x5o8gwb.fsf-monnier+emacs@gnu.org> (Stefan Monnier's message of "Sat\, 27 Oct 2007 16\:04\:57 -0400")

>> Thank you for your appreciation. But my modification is in the C code of
>> emacs, because 'execute-command' and 'call-interactively' are in C code.
>> And I don't think my dirty code would be a valid patch for the emacs
>> developers.
>
> I think what was expected was to first record a keyboard macro and later to
> turn that into elisp code.

That's was exactly my first attempt when I tried to implement this feature.
However, this approach doesn't work because a macro highly depends on its
context, and will fail when repeating it in different buffers, modes, etc.

> Another approach is to use a pre-command-hook to record the value of
> `this-command' for each command run.

After failing with the first approach, I tried to do this, but this doesn't
work because `this-command' doesn't record the arguments of the last
command.  So when I added a new variable `this-command-args' that records
the arguments of the command being executed, this approach produced
good results.

Also I added a new variable `last-kbd-macro-commands', and a new command
`insert-last-kbd-macro-commands' to convert the recorded commands with
their arguments to a Lisp function.  A change in isearch was also required
to convert all isearch subcommands into one search function.

Since yzhh doesn't want to post his code, I will post mine.
I ask yzhh to comment on this code, compare with his own,
and suggest further improvements:

Index: src/callint.c
===================================================================
RCS file: /sources/emacs/emacs/src/callint.c,v
retrieving revision 1.157
diff -c -r1.157 callint.c
*** src/callint.c	13 Sep 2007 05:50:11 -0000	1.157
--- src/callint.c	27 Oct 2007 21:44:37 -0000
***************
*** 42,47 ****
--- 42,48 ----
  
  extern Lisp_Object Vhistory_length;
  extern Lisp_Object Vthis_original_command, real_this_command;
+ extern Lisp_Object Vthis_command_args;
  
  Lisp_Object Vcommand_debug_status, Qcommand_debug_status;
  Lisp_Object Qenable_recursive_minibuffers;
***************
*** 364,369 ****
--- 365,373 ----
  	     and turn them into things we can eval.  */
  	  values = quotify_args (Fcopy_sequence (specs));
  	  fix_command (input, values);
+ 
+ 	  Vthis_command_args = values;
+ 
  	  Vcommand_history
  	    = Fcons (Fcons (function, values), Vcommand_history);
  
***************
*** 808,813 ****
--- 812,820 ----
  	  else
  	    visargs[i] = quotify_arg (args[i]);
  	}
+ 
+       Vthis_command_args = XCDR (Flist (count + 1, visargs));
+ 
        Vcommand_history = Fcons (Flist (count + 1, visargs),
  				Vcommand_history);
        /* Don't keep command history around forever.  */
***************
*** 832,837 ****
--- 839,846 ----
    Vthis_original_command = save_this_original_command;
    real_this_command= save_real_this_command;
    current_kboard->Vlast_command = save_last_command;
+   if (NILP (Vthis_command_args))
+     Vthis_command_args = XCDR (Flist (count + 1, args));
  
    {
      Lisp_Object val;
Index: src/keyboard.c
===================================================================
RCS file: /sources/emacs/emacs/src/keyboard.c,v
retrieving revision 1.924
diff -c -r1.924 keyboard.c
*** src/keyboard.c	18 Oct 2007 22:07:33 -0000	1.924
--- src/keyboard.c	27 Oct 2007 21:44:41 -0000
***************
*** 371,376 ****
--- 371,379 ----
     command is stored in this-original-command.  It is nil otherwise.  */
  Lisp_Object Vthis_original_command;
  
+ /* The 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;
  
***************
*** 1765,1770 ****
--- 1768,1774 ----
  
        Vthis_command = cmd;
        real_this_command = cmd;
+       Vthis_command_args = Qnil;
        /* Note that the value cell will never directly contain nil
  	 if the symbol is a local variable.  */
        if (!NILP (Vpre_command_hook) && !NILP (Vrun_hooks))
***************
*** 1798,1803 ****
--- 1802,1808 ----
  		    = window_display_table (XWINDOW (selected_window));
  		  lose = FETCH_CHAR (PT_BYTE);
  		  SET_PT (PT + 1);
+ 		  Vthis_command_args = Fcons (Vcurrent_prefix_arg, Qnil);
  		  if (! NILP (Vpost_command_hook))
  		    /* Put this before calling adjust_point_for_property
  		       so it will only get called once in any case.  */
***************
*** 1838,1843 ****
--- 1843,1849 ----
  		    = window_display_table (XWINDOW (selected_window));
  		  SET_PT (PT - 1);
  		  lose = FETCH_CHAR (PT_BYTE);
+ 		  Vthis_command_args = Fcons (Vcurrent_prefix_arg, Qnil);
  		  if (! NILP (Vpost_command_hook))
  		    goto directly_done;
  		  if (current_buffer == prev_buffer
***************
*** 1905,1910 ****
--- 1911,1917 ----
  		  if (value == 2)
  		    nonundocount = 0;
  
+ 		  Vthis_command_args = Fcons (make_number (c), Fcons (Vcurrent_prefix_arg, Qnil));
  		  if (! NILP (Vpost_command_hook))
  		    /* Put this before calling adjust_point_for_property
  		       so it will only get called once in any case.  */
***************
*** 3183,3189 ****
  #endif
        
        last_input_char = c;
!       Fcommand_execute (tem, Qnil, Fvector (1, &last_input_char), Qt);
  
        if (CONSP (c) && EQ (XCAR (c), Qselect_window) && !end_time)
  	/* We stopped being idle for this event; undo that.  This
--- 3190,3197 ----
  #endif
        
        last_input_char = c;
!       Fcommand_execute (tem, Qnil, /* Qt, */
! 			Fvector (1, &last_input_char), Qt);
  
        if (CONSP (c) && EQ (XCAR (c), Qselect_window) && !end_time)
  	/* We stopped being idle for this event; undo that.  This
***************
*** 12000,12005 ****
--- 12008,12017 ----
  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.  */);

Index: lisp/kmacro.el
===================================================================
RCS file: /sources/emacs/emacs/lisp/kmacro.el,v
retrieving revision 1.40
diff -c -r1.40 kmacro.el
*** lisp/kmacro.el	26 Jul 2007 05:26:27 -0000	1.40
--- lisp/kmacro.el	27 Oct 2007 21:44:40 -0000
***************
*** 538,545 ****
  
  (put 'kmacro-delete-ring-head 'kmacro-repeat 'head)
  
! ;;; Traditional bindings:
  
  
  ;;;###autoload
  (defun kmacro-start-macro (arg)
--- 538,548 ----
  
  (put 'kmacro-delete-ring-head 'kmacro-repeat 'head)
  
! (defvar last-kbd-macro-commands nil
!   "List of recorded commands executed during macro definition.
! Each command is represented as a form to evaluate.")
  
+ ;;; Traditional bindings:
  
  ;;;###autoload
  (defun kmacro-start-macro (arg)
***************
*** 580,592 ****
  	      kmacro-counter-format kmacro-default-counter-format
  	      kmacro-counter-format-start kmacro-default-counter-format))
  
        (start-kbd-macro append
  		       (and append
  			    (if kmacro-execute-before-append
  				(> (car arg) 4)
  			      (= (car arg) 4))))
        (if (and defining-kbd-macro append)
! 	  (setq defining-kbd-macro 'append)))))
  
  
  ;;;###autoload
--- 583,599 ----
  	      kmacro-counter-format kmacro-default-counter-format
  	      kmacro-counter-format-start kmacro-default-counter-format))
  
+       (add-hook 'post-command-hook 'kmacro-record-command t)
+       (add-hook 'kbd-macro-termination-hook 'kmacro-record-termination)
+ 
        (start-kbd-macro append
  		       (and append
  			    (if kmacro-execute-before-append
  				(> (car arg) 4)
  			      (= (car arg) 4))))
        (if (and defining-kbd-macro append)
! 	  (setq defining-kbd-macro 'append)
! 	(setq last-kbd-macro-commands nil)))))
  
  
  ;;;###autoload
***************
*** 605,610 ****
--- 612,618 ----
     ;; Just ignore it when executing the macro.
    (unless executing-kbd-macro
      (end-kbd-macro arg #'kmacro-loop-setup-function)
+     (kmacro-record-termination)
      (when (and last-kbd-macro (= (length last-kbd-macro) 0))
        (setq last-kbd-macro nil)
        (message "Ignore empty macro")
***************
*** 901,906 ****
--- 909,955 ----
    (kmacro-push-ring)
    (edit-kbd-macro "\C-hl"))
  
+ ;;; Macro commands recording
+ 
+ (defun kmacro-record-command ()
+   (unless (active-minibuffer-window)
+     (setq last-kbd-macro-commands (cons (cons this-command this-command-args)
+ 					last-kbd-macro-commands))))
+ 
+ (defun kmacro-record-termination ()
+   (remove-hook 'post-command-hook 'kmacro-record-command)
+   (remove-hook 'kbd-macro-termination-hook 'kmacro-record-termination))
+ 
+ (defun insert-last-kbd-macro-commands ()
+   (interactive)
+   (insert (pp-to-string
+ 	   `(defun last-kbd-macro-commands ()
+ 	      "Command created from the last keyboard macro."
+ 	      (interactive)
+ 	      ,@(kmacro-convert-macro-commands last-kbd-macro-commands)))))
+ 
+ (defun kmacro-convert-macro-commands (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
+ 		     universal-argument universal-argument-other-key
+ 		     digit-argument
+ 		     isearch-forward isearch-backward
+ 		     isearch-forward-regexp isearch-backward-regexp
+ 		     isearch-printing-char isearch-other-control-char
+ 		     isearch-repeat-forward isearch-repeat-backward
+ 		     isearch-delete-char isearch-exit
+ 		     )))
+        ((eq name 'self-insert-command)
+ 	(push `(insert-char ,(nth 1 cmd) ,(or (nth 2 cmd) 1) ) ret))
+        (t (push cmd ret)))
+       (setq cmds (cdr cmds)))
+     ret))
  
  ;;; Single-step editing of keyboard macros

Index: lisp/isearch.el
===================================================================
RCS file: /sources/emacs/emacs/lisp/isearch.el,v
retrieving revision 1.304
diff -u -r1.304 isearch.el
--- lisp/isearch.el	22 Oct 2007 23:44:29 -0000	1.304
+++ lisp/isearch.el	27 Oct 2007 21:44:03 -0000
*** 761,766 ****
--- 761,768 ----
    ;; part of the composition has just been searched.
    (setq disable-point-adjustment t))
  
+ (defvar last-kbd-macro-commands)
+ 
  (defun isearch-done (&optional nopush edit)
    "Exit Isearch mode.
  For successful search, pass no args.
***************
*** 776,781 ****
--- 778,794 ----
  	(unless (equal (car command-history) command)
  	  (setq command-history (cons command command-history)))))
  
+   (if (and (boundp 'last-kbd-macro-commands) defining-kbd-macro)
+       (push (list
+ 	     (cond (isearch-regexp
+ 		    (if isearch-forward 're-search-forward 're-search-backward))
+ 		   (isearch-word
+ 		    (if isearch-forward 'word-search-forward 'word-search-backward))
+ 		   (t
+ 		    (if isearch-forward 'search-forward 'search-backward)))
+ 	     isearch-string nil t)
+ 	    last-kbd-macro-commands))
+ 
    (remove-hook 'mouse-leave-buffer-hook 'isearch-done)
    (remove-hook 'kbd-macro-termination-hook 'isearch-done)
    (setq isearch-lazy-highlight-start nil)
  
-- 
Juri Linkov
http://www.jurta.org/emacs/

  parent reply	other threads:[~2007-10-27 21:45 UTC|newest]

Thread overview: 37+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2007-10-27  9:28 Is there a plan to record kbd macro as elisp code? yzhh
2007-10-27 15:48 ` Robert J. Chassell
2007-10-27 17:30   ` yzhh
2007-10-27 18:01     ` Drew Adams
2007-10-28 13:50       ` Richard Stallman
2007-10-27 20:04     ` Stefan Monnier
2007-10-27 21:22       ` Kim F. Storm
2007-10-28 13:50         ` Richard Stallman
2007-10-28 22:00           ` Kim F. Storm
2007-10-29  5:20             ` yzhh
2007-10-29  9:22             ` Richard Stallman
2007-10-29  9:22             ` Richard Stallman
2007-10-27 21:45       ` Juri Linkov [this message]
2007-10-28  1:14         ` Stefan Monnier
2007-10-28  1:34           ` Juri Linkov
2007-10-29  0:11             ` Richard Stallman
2007-10-28  6:49         ` yzhh
2007-10-28  7:13           ` yzhh
2007-10-28 10:54           ` Juri Linkov
2007-10-28 13:50         ` Richard Stallman
2007-10-28 15:09           ` Juri Linkov
2007-10-29  9:21             ` Richard Stallman
2007-10-28 16:13           ` yzhh
2007-10-28 16:48             ` Juri Linkov
2007-10-29  9:21             ` Richard Stallman
2007-10-30 14:14               ` Juri Linkov
2007-10-31  7:46                 ` Richard Stallman
2007-10-27 19:26   ` Jay Belanger
2007-10-27 16:20 ` Drew Adams
2007-10-27 17:13   ` yzhh
2007-10-27 17:40     ` Drew Adams
2007-10-27 18:05       ` yzhh
2007-10-27 19:22       ` Robert J. Chassell
2007-10-27 20:11         ` Drew Adams
2007-10-28 13:50   ` Richard Stallman
2007-10-28 16:45     ` Juri Linkov
2007-10-29  6:41 ` Klaus Zeitler

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

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

  git send-email \
    --in-reply-to=87y7doxmcw.fsf@jurta.org \
    --to=juri@jurta.org \
    --cc=emacs-devel@gnu.org \
    --cc=monnier@iro.umontreal.ca \
    --cc=yezonghui@gmail.com \
    /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 external index

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

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.