1 ;; Keyboard macro editor for GNU Emacs. Version 1.05.
2 ;; Copyright (C) 1990, 1991, 1992, 1993 Free Software Foundation, Inc.
3 ;; Written by Dave Gillespie, daveg@synaptics.com.
5 ;; This file is part of GNU Emacs.
7 ;; GNU Emacs is distributed in the hope that it will be useful,
8 ;; but WITHOUT ANY WARRANTY. No author or distributor
9 ;; accepts responsibility to anyone for the consequences of using it
10 ;; or for whether it serves any particular purpose or works at all,
11 ;; unless he says so in writing. Refer to the GNU Emacs General Public
12 ;; License for full details.
14 ;; Everyone is granted permission to copy, modify and redistribute
15 ;; GNU Emacs, but only under the conditions described in the
16 ;; GNU Emacs General Public License. A copy of this license is
17 ;; supposed to have been given to you along with GNU Emacs so you
18 ;; can know your rights and responsibilities. It should be in a
19 ;; file named COPYING. Among other things, the copyright notice
20 ;; and this notice must be preserved on all copies.
23 ;; (autoload 'edit-kbd-macro "macedit" "Edit a named keyboard macro" t)
24 ;; (autoload 'edit-last-kbd-macro "macedit" "Edit a keyboard macro" t)
25 ;; (autoload 'read-kbd-macro "macedit" "Parse region as keyboard macro" t)
27 ;; Note: In XEmacs (at least) these functions are defined in
28 ;; `edmacro.el' so as to not pollute the namespace they have been
29 ;; commented out here. youngs@xemacs.org 2002-04-30
31 ;; To use, type `M-x edit-last-kbd-macro' to edit the most recently
32 ;; defined keyboard macro. If you have used `M-x name-last-kbd-macro'
33 ;; to give a keyboard macro a name, type `M-x edit-kbd-macro' to edit
34 ;; the macro by name. When you are done editing, type `C-c C-c' to
35 ;; record your changes back into the original keyboard macro.
40 ;;; The user-level commands for editing macros.
42 ;; These functions are all defined in `edmacro.el'. youngs@xemacs.org
45 ;; (defun edit-last-kbd-macro (&optional prefix buffer hook)
46 ;; "Edit the most recently defined keyboard macro."
48 ;; (MacEdit-edit-macro last-kbd-macro
49 ;; (function (lambda (x arg) (setq last-kbd-macro x)))
50 ;; prefix buffer hook)
54 ;; (defun edit-kbd-macro (cmd &optional prefix buffer hook in-hook out-hook)
55 ;; "Edit a keyboard macro which has been assigned a name by name-last-kbd-macro.
56 ;; \(See also edit-last-kbd-macro.)"
57 ;; (interactive "CCommand name: \nP")
59 ;; (MacEdit-edit-macro (if in-hook
60 ;; (funcall in-hook cmd)
61 ;; (symbol-function cmd))
63 ;; (list 'lambda '(x arg)
67 ;; prefix buffer hook cmd))
71 ;; (defun read-kbd-macro (start &optional end)
72 ;; "Read the region as a keyboard macro definition.
73 ;; The region is interpreted as spelled-out keystrokes, e.g., `M-x abc RET'.
74 ;; The resulting macro is installed as the \"current\" keyboard macro.
76 ;; Symbols: RET, SPC, TAB, DEL, LFD, NUL; C-key; M-key. (Must be uppercase.)
77 ;; REM marks the rest of a line as a comment.
78 ;; Whitespace is ignored; other characters are copied into the macro."
80 ;; (if (stringp start)
81 ;; (setq last-kbd-macro (MacEdit-parse-keys start))
82 ;; (setq last-kbd-macro (MacEdit-parse-keys (buffer-substring start end)))
83 ;; (if (and (string-match "\\`\C-x(" last-kbd-macro)
84 ;; (string-match "\C-x)\\'" last-kbd-macro))
85 ;; (setq last-kbd-macro (substring last-kbd-macro 2 -2))))
91 ;;; Formatting a keyboard macro as human-readable text.
93 (defun MacEdit-print-macro (macro-str local-map)
94 (let ((save-map (current-local-map))
95 (print-escape-newlines t)
96 key-symbol key-str key-last prefix-arg this-prefix)
99 (use-local-map local-map)
100 (while (MacEdit-peek-char)
102 (setq this-prefix prefix-arg)
103 (or (memq key-symbol '(digit-argument
108 (cond ((consp prefix-arg)
109 (insert (format "prefix-arg (%d)\n"
112 (insert "prefix-arg -\n"))
113 ((numberp prefix-arg)
114 (insert (format "prefix-arg %d\n" prefix-arg))))
115 (setq prefix-arg nil)))
116 (cond ((null key-symbol)
118 (MacEdit-insert-string macro-str)
121 ((stringp key-symbol) ; key defined by another kbd macro
123 (MacEdit-insert-string key-symbol)
125 ((eq key-symbol 'digit-argument)
126 (MacEdit-prefix-arg key-last nil prefix-arg))
127 ((eq key-symbol 'negative-argument)
128 (MacEdit-prefix-arg ?- nil prefix-arg))
129 ((eq key-symbol 'universal-argument)
130 (let* ((c-u 4) (argstartchar key-last)
131 (char (MacEdit-read-char)))
132 (while (= char argstartchar)
134 char (MacEdit-read-char)))
135 (MacEdit-prefix-arg char c-u nil)))
136 ((eq key-symbol 'self-insert-command)
138 (if (and (>= key-last 32) (<= key-last 126))
140 (while (or (and (eq key-symbol
141 'self-insert-command)
145 (and (memq key-symbol
146 '(backward-delete-char
148 backward-delete-char-untabify))
150 (if (eq key-symbol 'self-insert-command)
151 (setq str (concat str
152 (char-to-string key-last)))
153 (setq str (substring str 0 -1)))
155 (insert "\"" str "\"\n")
156 (MacEdit-unread-chars key-str))
158 (MacEdit-insert-string (char-to-string key-last))
160 ((and (eq key-symbol 'quoted-insert)
162 (insert "quoted-insert\n")
163 (let ((ch (MacEdit-read-char))
165 (if (and (>= ch ?0) (<= ch ?7))
168 ch2 (MacEdit-read-char))
170 (if (and (>= ch2 ?0) (<= ch2 ?7))
172 (setq ch (+ (* ch 8) (- ch2 ?0))
173 ch2 (MacEdit-read-char))
175 (if (and (>= ch2 ?0) (<= ch2 ?7))
176 (setq ch (+ (* ch 8) (- ch2 ?0)))
177 (MacEdit-unread-chars ch2))))
178 (MacEdit-unread-chars ch2)))))
179 (if (or (and (>= ch ?0) (<= ch ?7))
180 (< ch 32) (> ch 126))
181 (insert (format "type \"\\%03o\"\n" ch))
182 (insert "type \"" (char-to-string ch) "\"\n"))))
183 ((memq key-symbol '(isearch-forward
185 isearch-forward-regexp
186 isearch-backward-regexp))
187 (insert (symbol-name key-symbol) "\n")
188 (MacEdit-isearch-argument))
189 ((eq key-symbol 'execute-extended-command)
190 (MacEdit-read-argument obarray 'commandp))
192 (let ((cust (get key-symbol 'MacEdit-print)))
195 (insert (symbol-name key-symbol))
198 (MacEdit-insert-string key-str)
200 (let ((int (MacEdit-get-interactive key-symbol)))
201 (if (string-match "\\`\\*" int)
202 (setq int (substring int 1)))
203 (while (> (length int) 0)
204 (cond ((= (aref int 0) ?a)
205 (MacEdit-read-argument
207 ((memq (aref int 0) '(?b ?B ?D ?f ?F ?n
209 (MacEdit-read-argument))
210 ((and (= (aref int 0) ?c)
213 (MacEdit-insert-string
215 (MacEdit-read-char)))
218 (MacEdit-read-argument
225 (MacEdit-insert-string key-str)
227 (MacEdit-unread-chars key-str)))
230 (MacEdit-read-argument)))
232 (MacEdit-read-argument
233 obarray 'user-variable-p)))
234 (let ((nl (string-match "\n" int)))
236 (substring int (1+ nl))
238 (use-local-map save-map)))
241 (defun MacEdit-prefix-arg (char c-u value)
243 (if (and (numberp value) (< value 0))
244 (setq sign -1 value (- value)))
246 (setq sign -1 value nil))
247 (while (and char (= ?- char))
248 (setq sign (- sign) c-u nil)
249 (setq char (MacEdit-read-char)))
250 (while (and char (>= char ?0) (<= char ?9))
251 (setq value (+ (* (if (numberp value) value 0) 10) (- char ?0)) c-u nil)
252 (setq char (MacEdit-read-char)))
254 (cond (c-u (list c-u))
255 ((numberp value) (* value sign))
257 (MacEdit-unread-chars char))
260 (defun MacEdit-insert-string (str)
262 (while (< i (length str))
263 (if (and (> (setq ch (aref str i)) 127)
269 (cond ((= ch 8) (insert "\\b"))
270 ((= ch 9) (insert "\\t"))
271 ((= ch 10) (insert "\\n"))
272 ((= ch 13) (insert "\\r"))
273 ((= ch 27) (insert "\\e"))
274 (t (insert "\\C-" (char-to-string (downcase (+ ch 64))))))
276 (if (or (= ch 34) (= ch 92))
277 (insert "\\" (char-to-string ch))
279 (while (and (< (setq i (1+ i)) (length str))
280 (>= (setq ch (aref str i)) 32)
281 (/= ch 34) (/= ch 92)
283 (insert (substring str j i))
285 (if (memq ch '(?\177 ?\377))
286 (insert (format "\\%03o" ch))
287 (insert "\\M-" (char-to-string (- ch 128))))))
291 (defun MacEdit-lookup-key (map)
292 (let ((loc (and map (lookup-key map macro-str)))
293 (glob (lookup-key (current-global-map) macro-str))
295 (glob-str macro-str))
297 (setq loc-str (substring macro-str 0 loc)
298 loc (lookup-key map loc-str)))
304 (setq glob-str (substring macro-str 0 glob)
305 glob (lookup-key (current-global-map) glob-str)))
310 (if (> (length glob-str) (length loc-str))
311 (setq key-symbol glob
315 (setq key-last (and (> (length key-str) 0)
316 (logand (aref key-str (1- (length key-str))) 127)))
320 (defun MacEdit-read-argument (&optional obarray pred) ;; currently ignored
323 (exec (eq key-symbol 'execute-extended-command))
326 (MacEdit-lookup-key (current-global-map))
327 (or (and (eq key-symbol 'self-insert-command)
330 '(backward-delete-char
332 backward-delete-char-untabify))
334 (setq macro-str (substring macro-str (length key-str)))
335 (or (and (eq key-last 9)
337 (let ((comp (try-completion str obarray pred)))
339 (> (length comp) (length str))
341 (if (or (eq key-symbol 'self-insert-command)
342 (and (or (eq key-last 9)
343 (<= (length str) min-bsp))
344 (setq min-bsp (+ (length str) (length key-str)))))
345 (setq str (concat str key-str))
346 (setq str (substring str 0 -1)))))
348 str (concat str key-str)
349 macro-str (substring macro-str (length key-str)))
351 (let ((comp (try-completion str-base obarray pred)))
352 (if (if (stringp comp)
353 (and (commandp (intern comp))
354 (setq str-base comp))
355 (commandp (intern str-base)))
356 (insert str-base "\n")
357 (insert "execute-extended-command\n")
359 (MacEdit-insert-string str)
361 (if (> (length str) 0)
364 (MacEdit-insert-string str)
368 (defun MacEdit-isearch-argument ()
372 (while (and (setq ch (MacEdit-read-char))
373 (or (<= ch 127) (not search-exit-option))
374 (not (eq ch search-exit-char))
375 (or (eq ch search-repeat-char)
376 (eq ch search-reverse-char)
377 (eq ch search-delete-char)
378 (eq ch search-yank-word-char)
379 (eq ch search-yank-line-char)
380 (eq ch search-quote-char)
383 (not search-exit-option)
384 (and (/= ch 127) (>= ch 32))))
385 (if (and (eq ch search-quote-char)
387 (setq str (concat str (char-to-string ch)
388 (char-to-string (MacEdit-read-char)))
389 min-bsp (length str))
390 (if (or (and (< ch 127) (>= ch 32))
391 (eq ch search-yank-word-char)
392 (eq ch search-yank-line-char)
393 (and (or (not (eq ch search-delete-char))
394 (<= (length str) min-bsp))
395 (setq min-bsp (1+ (length str)))))
396 (setq str (concat str (char-to-string ch)))
397 (setq str (substring str 0 -1)))))
398 (if (eq ch search-exit-char)
399 (if (= (length str) 0) ;; non-incremental search
401 (setq str (concat str (char-to-string ch)))
402 (and (eq (MacEdit-peek-char) ?\C-w)
404 (setq str (concat str "\C-w"))
405 (MacEdit-read-char)))
406 (if (> (length str) 0)
409 (MacEdit-insert-string str)
411 (MacEdit-read-argument)
413 (MacEdit-unread-chars ch))
414 (if (> (length str) 0)
417 (MacEdit-insert-string str)
418 (insert "\\e\"\n"))))
421 ;;; Get the next keystroke-sequence from the input stream.
422 ;;; Sets key-symbol, key-str, and key-last as a side effect.
423 (defun MacEdit-read-key ()
424 (MacEdit-lookup-key (current-local-map))
426 (setq macro-str (substring macro-str (length key-str))))
429 (defun MacEdit-peek-char ()
430 (and (> (length macro-str) 0)
434 (defun MacEdit-read-char ()
435 (and (> (length macro-str) 0)
438 (setq macro-str (substring macro-str 1))))
441 (defun MacEdit-unread-chars (chars)
442 (and (integerp chars)
443 (setq chars (char-to-string chars)))
445 (setq macro-str (concat chars macro-str)))
448 (defun MacEdit-dump (mac)
449 (set-mark-command nil)
451 (MacEdit-print-macro mac (current-local-map))
456 ;;; Parse a string of spelled-out keystrokes, as produced by key-description.
458 (defun MacEdit-parse-keys (str)
462 (while (and (< pos (length str))
463 (string-match "[^ \t\n]+" str pos))
464 (setq pos (match-end 0)
465 part (substring str (match-beginning 0) (match-end 0))
467 (if (and (> (length part) 2)
469 (= (aref part 0) ?M))
471 (setq part (substring part 2))
473 (if (and (> (length part) 4)
477 (= (aref part 3) ?-))
479 (setq part (concat "C-" (substring part 4)))
482 (or (cdr (assoc part '( ( "NUL" . "\0" )
491 ( "C-SPC" . "\0") )))
492 (and (equal part "REM")
493 (setq pos (or (string-match "\n" str pos)
496 (and (= (length part) 3)
499 (char-to-string (logand (aref part 2) 31)))
507 ;;; Parse a keyboard macro description in MacEdit-print-macro's format.
509 (defun MacEdit-read-macro (&optional map)
510 (or map (setq map (current-local-map)))
511 (let ((macro-str ""))
513 (skip-chars-forward " \t\n")
515 (cond ((looking-at "#")) ;; comment
516 ((looking-at "prefix-arg[ \t]*-[ \t]*\n")
517 (MacEdit-append-chars "\C-u-"))
518 ((looking-at "prefix-arg[ \t]*\\(-?[0-9]+\\)[ \t]*\n")
519 (MacEdit-append-chars (concat "\C-u" (MacEdit-match-string 1))))
520 ((looking-at "prefix-arg[ \t]*(\\([0-9]+\\))[ \t]*\n")
521 (let ((val (string-to-int (MacEdit-match-string 1))))
524 (error "Bad prefix argument value"))
525 (MacEdit-append-chars "\C-u")
526 (setq val (/ val 4)))))
527 ((looking-at "prefix-arg")
528 (error "Bad prefix argument syntax"))
529 ((looking-at "insert ")
531 (MacEdit-append-chars (read (current-buffer)))
532 (if (< (current-column) 7)
534 ((looking-at "type ")
536 (MacEdit-append-chars (read (current-buffer)))
537 (if (< (current-column) 5)
539 ((looking-at "keys \\(.*\\)\n")
540 (goto-char (1- (match-end 0)))
541 (MacEdit-append-chars (MacEdit-parse-keys
542 (buffer-substring (match-beginning 1)
544 ((looking-at "\\([-a-zA-z0-9_]+\\)[ \t]*\\(.*\\)\n")
545 (let* ((func (intern (MacEdit-match-string 1)))
546 (arg (MacEdit-match-string 2))
547 (cust (get func 'MacEdit-read)))
551 (error "Not an Emacs command"))
553 (string-match "\\`#" arg)
554 (error "Unexpected argument to command"))
556 (or (where-is-internal func map t)
557 (where-is-internal func (current-global-map) t))))
559 (MacEdit-append-chars keys)
560 (MacEdit-append-chars (concat "\ex"
563 (t (error "Syntax error")))
568 (defun MacEdit-append-chars (chars)
569 (setq macro-str (concat macro-str chars))
572 (defun MacEdit-match-string (n)
573 (if (match-beginning n)
574 (buffer-substring (match-beginning n) (match-end n))
580 (defun MacEdit-get-interactive (func)
582 (let ((cust (get func 'MacEdit-interactive)))
585 (MacEdit-get-interactive (symbol-function func))))
586 (or (and (eq (car-safe func) 'lambda)
587 (let ((int (if (consp (nth 2 func))
590 (and (eq (car-safe int) 'interactive)
591 (stringp (nth 1 int))
596 (put 'search-forward 'MacEdit-interactive "s")
597 (put 'search-backward 'MacEdit-interactive "s")
598 (put 'word-search-forward 'MacEdit-interactive "s")
599 (put 'word-search-backward 'MacEdit-interactive "s")
600 (put 're-search-forward 'MacEdit-interactive "s")
601 (put 're-search-backward 'MacEdit-interactive "s")
602 (put 'switch-to-buffer 'MacEdit-interactive "B")
603 (put 'kill-buffer 'MacEdit-interactive "B")
604 (put 'rename-buffer 'MacEdit-interactive "B\nB")
605 (put 'goto-char 'MacEdit-interactive "N")
606 (put 'global-set-key 'MacEdit-interactive "k\nC")
607 (put 'global-unset-key 'MacEdit-interactive "k")
608 (put 'local-set-key 'MacEdit-interactive "k\nC")
609 (put 'local-unset-key 'MacEdit-interactive "k")
611 ;;; Think about kbd-macro-query
615 ;;; Edit a keyboard macro in another buffer.
616 ;;; (Prefix argument is currently ignored.)
618 (defun MacEdit-edit-macro (mac repl &optional prefix buffer hook arg)
620 (error "Not a keyboard macro"))
621 (let ((oldbuf (current-buffer))
622 (from-calc (and (get-buffer-window "*Calculator*")
623 (eq (lookup-key (current-global-map) "\e#")
625 (local (current-local-map))
626 (buf (get-buffer-create (or buffer "*Edit Macro*"))))
628 (kill-all-local-variables)
629 (use-local-map MacEdit-mode-map)
630 (setq buffer-read-only nil)
631 (setq major-mode 'MacEdit-mode)
632 (setq mode-name "Edit Macro")
633 (make-local-variable 'MacEdit-original-buffer)
634 (setq MacEdit-original-buffer oldbuf)
635 (make-local-variable 'MacEdit-replace-function)
636 (setq MacEdit-replace-function repl)
637 (make-local-variable 'MacEdit-replace-argument)
638 (setq MacEdit-replace-argument arg)
639 (make-local-variable 'MacEdit-finish-hook)
640 (setq MacEdit-finish-hook hook)
642 (insert "# Keyboard Macro Editor. Press "
643 (if from-calc "M-# M-#" "C-c C-c")
645 (if from-calc "M-# x" "C-x k RET")
647 (insert "# Original keys: " (key-description mac) "\n\n")
648 (message "Formatting keyboard macro...")
649 (MacEdit-print-macro mac local)
650 (switch-to-buffer buf)
651 (goto-char (point-min))
654 (set-buffer-modified-p nil)
655 (message "Formatting keyboard macro...done")
656 (run-hooks 'MacEdit-format-hook))
659 (defun MacEdit-finish-edit ()
661 (or (and (boundp 'MacEdit-original-buffer)
662 (boundp 'MacEdit-replace-function)
663 (boundp 'MacEdit-replace-argument)
664 (boundp 'MacEdit-finish-hook)
665 (eq major-mode 'MacEdit-mode))
666 (error "This command is valid only in buffers created by edit-kbd-macro."))
667 (let ((buf (current-buffer))
668 (str (buffer-string))
669 (func MacEdit-replace-function)
670 (arg MacEdit-replace-argument)
671 (hook MacEdit-finish-hook))
672 (goto-char (point-min))
673 (and (buffer-modified-p)
676 (message "Compiling keyboard macro...")
677 (run-hooks 'MacEdit-compile-hook)
678 (let ((mac (MacEdit-read-macro
679 (and (buffer-name MacEdit-original-buffer)
681 (set-buffer MacEdit-original-buffer)
682 (current-local-map))))))
683 (and (buffer-name MacEdit-original-buffer)
684 (switch-to-buffer MacEdit-original-buffer))
685 (funcall func mac arg))
686 (message "Compiling keyboard macro...done")))
692 (defun MacEdit-cancel-edit ()
694 (if (eq major-mode 'MacEdit-mode)
695 (set-buffer-modified-p nil))
696 (MacEdit-finish-edit)
697 (message "(Cancelled)")
700 (defun MacEdit-mode ()
701 "Keyboard Macro Editing mode. Press C-c C-c to save and exit.
702 To abort the edit, just kill this buffer with C-x k RET.
704 The keyboard macro is represented as a series of M-x style command names.
705 Keystrokes which do not correspond to simple M-x commands are written as
706 \"type\" commands. When you press C-c C-c, MacEdit converts each command
707 back into a suitable keystroke sequence; \"type\" commands are converted
708 directly back into keystrokes."
710 (error "This mode can be enabled only by edit-kbd-macro or edit-last-kbd-macro.")
712 (put 'MacEdit-mode 'mode-class 'special)
714 (defvar MacEdit-mode-map nil)
717 (setq MacEdit-mode-map (make-sparse-keymap))
718 (define-key MacEdit-mode-map "\C-c\C-c" 'MacEdit-finish-edit)