emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Is there a plan to record kbd macro as elisp code?


From: Juri Linkov
Subject: Re: Is there a plan to record kbd macro as elisp code?
Date: Sun, 28 Oct 2007 00:45:45 +0300
User-agent: Gnus/5.11 (Gnus v5.11) Emacs/23.0.50 (gnu/linux)

>> 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/




reply via email to

[Prev in Thread] Current Thread [Next in Thread]