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/
next prev 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
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=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 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).