22
|
1 ;;; edmacro.el --- keyboard macro editor
|
|
2
|
|
3 ;; Copyright (C) 1993, 1994 Free Software Foundation, Inc.
|
|
4
|
|
5 ;; Author: Dave Gillespie <daveg@synaptics.com>
|
24
|
6 ;; Hrvoje Niksic <hniksic@srce.hr> -- XEmacs port
|
|
7 ;; Maintainer: Hrvoje Niksic <hniksic@srce.hr>
|
134
|
8 ;; Version: 3.09
|
22
|
9 ;; Keywords: abbrev
|
|
10
|
24
|
11 ;; This file is part of XEmacs.
|
22
|
12
|
24
|
13 ;; XEmacs is free software; you can redistribute it and/or modify
|
22
|
14 ;; it under the terms of the GNU General Public License as published by
|
|
15 ;; the Free Software Foundation; either version 2, or (at your option)
|
|
16 ;; any later version.
|
|
17
|
24
|
18 ;; XEmacs is distributed in the hope that it will be useful, but
|
|
19 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
20 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
21 ;; General Public License for more details.
|
22
|
22
|
|
23 ;; You should have received a copy of the GNU General Public License
|
24
|
24 ;; along with XEmacs; see the file COPYING. If not, write to the Free
|
|
25 ;; Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
26 ;; 02111-1307, USA.
|
|
27
|
|
28 ;;; Synched up with: FSF 19.34.
|
22
|
29
|
|
30 ;;; Commentary:
|
|
31
|
|
32 ;;; Usage:
|
|
33 ;;
|
|
34 ;; The `C-x C-k' (`edit-kbd-macro') command edits a keyboard macro
|
|
35 ;; in a special buffer. It prompts you to type a key sequence,
|
|
36 ;; which should be one of:
|
|
37 ;;
|
|
38 ;; * RET or `C-x e' (call-last-kbd-macro), to edit the most
|
|
39 ;; recently defined keyboard macro.
|
|
40 ;;
|
|
41 ;; * `M-x' followed by a command name, to edit a named command
|
|
42 ;; whose definition is a keyboard macro.
|
|
43 ;;
|
|
44 ;; * `C-h l' (view-lossage), to edit the 100 most recent keystrokes
|
|
45 ;; and install them as the "current" macro.
|
|
46 ;;
|
|
47 ;; * any key sequence whose definition is a keyboard macro.
|
|
48 ;;
|
|
49 ;; This file includes a version of `insert-kbd-macro' that uses the
|
|
50 ;; more readable format defined by these routines.
|
|
51 ;;
|
|
52 ;; Also, the `read-kbd-macro' command parses the region as
|
|
53 ;; a keyboard macro, and installs it as the "current" macro.
|
|
54 ;; This and `format-kbd-macro' can also be called directly as
|
|
55 ;; Lisp functions.
|
|
56
|
134
|
57 ;; The `kbd' function is a shorter name for `read-kbd-macro'. It is
|
|
58 ;; good to use in your programs and initializations, as you needn't
|
|
59 ;; know the internal keysym representation. For example:
|
118
|
60 ;;
|
|
61 ;; (define-key foo-mode-map (kbd "C-c <up>") 'foo-up)
|
134
|
62 ;;
|
118
|
63 ;; is the equivalent of
|
134
|
64 ;;
|
118
|
65 ;; (define-key foo-mode-map [(control ?c) up] 'foo-up)
|
134
|
66 ;;
|
118
|
67
|
22
|
68 ;; Type `C-h m', or see the documentation for `edmacro-mode' below,
|
|
69 ;; for information about the format of written keyboard macros.
|
|
70
|
|
71 ;; `edit-kbd-macro' formats the macro with one command per line,
|
|
72 ;; including the command names as comments on the right. If the
|
|
73 ;; formatter gets confused about which keymap was used for the
|
|
74 ;; characters, the command-name comments will be wrong but that
|
|
75 ;; won't hurt anything.
|
|
76
|
|
77 ;; With a prefix argument, `edit-kbd-macro' will format the
|
|
78 ;; macro in a more concise way that omits the comments.
|
|
79
|
|
80 ;; This package requires GNU Emacs 19 or later, and daveg's CL
|
|
81 ;; package 2.02 or later. (CL 2.02 comes standard starting with
|
|
82 ;; Emacs 19.18.) This package does not work with Emacs 18 or
|
|
83 ;; Lucid Emacs.
|
|
84
|
134
|
85 ;; Ported to XEmacs. -hniksic
|
22
|
86
|
|
87 ;;; Code:
|
|
88
|
|
89 (eval-when-compile
|
24
|
90 (require 'cl))
|
22
|
91
|
|
92 ;;; The user-level commands for editing macros.
|
|
93
|
|
94 ;;;###autoload (define-key ctl-x-map "\C-k" 'edit-kbd-macro)
|
|
95
|
|
96 ;;;###autoload
|
|
97 (defvar edmacro-eight-bits nil
|
|
98 "*Non-nil if edit-kbd-macro should leave 8-bit characters intact.
|
|
99 Default nil means to write characters above \\177 in octal notation.")
|
|
100
|
134
|
101 (if (fboundp 'mapvector)
|
|
102 (defalias 'edmacro-mapvector 'mapvector)
|
|
103 (defun edmacro-mapvector (fun seq)
|
|
104 (map 'vector fun seq)))
|
|
105
|
22
|
106 (defvar edmacro-mode-map nil)
|
|
107 (unless edmacro-mode-map
|
|
108 (setq edmacro-mode-map (make-sparse-keymap))
|
|
109 (define-key edmacro-mode-map "\C-c\C-c" 'edmacro-finish-edit)
|
|
110 (define-key edmacro-mode-map "\C-c\C-q" 'edmacro-insert-key))
|
|
111
|
|
112 (defvar edmacro-store-hook)
|
|
113 (defvar edmacro-finish-hook)
|
|
114 (defvar edmacro-original-buffer)
|
|
115
|
134
|
116 ;; A lot of cruft here, but I got it to work eventually. Could use
|
|
117 ;; some cleaning up.
|
22
|
118 ;;;###autoload
|
|
119 (defun edit-kbd-macro (keys &optional prefix finish-hook store-hook)
|
|
120 "Edit a keyboard macro.
|
|
121 At the prompt, type any key sequence which is bound to a keyboard macro.
|
|
122 Or, type `C-x e' or RET to edit the last keyboard macro, `C-h l' to edit
|
|
123 the last 100 keystrokes as a keyboard macro, or `M-x' to edit a macro by
|
|
124 its command name.
|
|
125 With a prefix argument, format the macro in a more concise way."
|
|
126 (interactive "kKeyboard macro to edit (C-x e, M-x, C-h l, or keys): \nP")
|
|
127 (when keys
|
24
|
128 (setq keys (edmacro-events-to-keys keys))
|
22
|
129 (let ((cmd (if (arrayp keys) (key-binding keys) keys))
|
|
130 (mac nil))
|
|
131 (cond (store-hook
|
|
132 (setq mac keys)
|
|
133 (setq cmd nil))
|
|
134 ((or (eq cmd 'call-last-kbd-macro)
|
24
|
135 (and (arrayp keys)
|
|
136 (= 1 (length keys))
|
|
137 (eq ?\r (aref keys 0))))
|
22
|
138 (or last-kbd-macro
|
|
139 (y-or-n-p "No keyboard macro defined. Create one? ")
|
|
140 (keyboard-quit))
|
|
141 (setq mac (or last-kbd-macro ""))
|
|
142 (setq cmd 'last-kbd-macro))
|
|
143 ((eq cmd 'execute-extended-command)
|
|
144 (setq cmd (read-command "Name of keyboard macro to edit: "))
|
|
145 (if (string-equal cmd "")
|
|
146 (error "No command name given"))
|
|
147 (setq mac (symbol-function cmd)))
|
|
148 ((eq cmd 'view-lossage)
|
|
149 (setq mac (recent-keys))
|
|
150 (setq cmd 'last-kbd-macro))
|
|
151 ((null cmd)
|
|
152 (error "Key sequence %s is not defined" (key-description keys)))
|
|
153 ((symbolp cmd)
|
|
154 (setq mac (symbol-function cmd)))
|
|
155 (t
|
|
156 (setq mac cmd)
|
|
157 (setq cmd nil)))
|
|
158 (unless (arrayp mac)
|
|
159 (error "Key sequence %s is not a keyboard macro"
|
|
160 (key-description keys)))
|
|
161 (message "Formatting keyboard macro...")
|
|
162 (let* ((oldbuf (current-buffer))
|
|
163 (mmac (edmacro-fix-menu-commands mac))
|
|
164 (fmt (edmacro-format-keys mmac 1))
|
|
165 (fmtv (edmacro-format-keys mmac (not prefix)))
|
|
166 (buf (get-buffer-create "*Edit Macro*")))
|
|
167 (message "Formatting keyboard macro...done")
|
|
168 (switch-to-buffer buf)
|
|
169 (kill-all-local-variables)
|
|
170 (use-local-map edmacro-mode-map)
|
|
171 (setq buffer-read-only nil)
|
|
172 (setq major-mode 'edmacro-mode)
|
|
173 (setq mode-name "Edit Macro")
|
|
174 (set (make-local-variable 'edmacro-original-buffer) oldbuf)
|
|
175 (set (make-local-variable 'edmacro-finish-hook) finish-hook)
|
|
176 (set (make-local-variable 'edmacro-store-hook) store-hook)
|
|
177 (erase-buffer)
|
|
178 (insert ";; Keyboard Macro Editor. Press C-c C-c to finish; "
|
|
179 "press C-x k RET to cancel.\n")
|
|
180 (insert ";; Original keys: " fmt "\n")
|
|
181 (unless store-hook
|
|
182 (insert "\nCommand: " (if cmd (symbol-name cmd) "none") "\n")
|
|
183 (let ((keys (where-is-internal (or cmd mac))))
|
|
184 (if keys
|
|
185 (while keys
|
|
186 (insert "Key: " (edmacro-format-keys (pop keys) 1) "\n"))
|
|
187 (insert "Key: none\n"))))
|
|
188 (insert "\nMacro:\n\n")
|
|
189 (save-excursion
|
|
190 (insert fmtv "\n"))
|
|
191 (recenter '(4))
|
|
192 (when (eq mac mmac)
|
|
193 (set-buffer-modified-p nil))
|
|
194 (run-hooks 'edmacro-format-hook)))))
|
|
195
|
|
196 ;;; The next two commands are provided for convenience and backward
|
|
197 ;;; compatibility.
|
|
198
|
|
199 ;;;###autoload
|
|
200 (defun edit-last-kbd-macro (&optional prefix)
|
|
201 "Edit the most recently defined keyboard macro."
|
|
202 (interactive "P")
|
|
203 (edit-kbd-macro 'call-last-kbd-macro prefix))
|
|
204
|
|
205 ;;;###autoload
|
|
206 (defun edit-named-kbd-macro (&optional prefix)
|
|
207 "Edit a keyboard macro which has been given a name by `name-last-kbd-macro'."
|
|
208 (interactive "P")
|
|
209 (edit-kbd-macro 'execute-extended-command prefix))
|
|
210
|
|
211 ;;;###autoload
|
|
212 (defun read-kbd-macro (start &optional end)
|
|
213 "Read the region as a keyboard macro definition.
|
|
214 The region is interpreted as spelled-out keystrokes, e.g., \"M-x abc RET\".
|
|
215 See documentation for `edmacro-mode' for details.
|
|
216 Leading/trailing \"C-x (\" and \"C-x )\" in the text are allowed and ignored.
|
|
217 The resulting macro is installed as the \"current\" keyboard macro.
|
|
218
|
|
219 In Lisp, may also be called with a single STRING argument in which case
|
|
220 the result is returned rather than being installed as the current macro.
|
|
221 The result will be a string if possible, otherwise an event vector.
|
|
222 Second argument NEED-VECTOR means to return an event vector always."
|
|
223 (interactive "r")
|
|
224 (if (stringp start)
|
|
225 (edmacro-parse-keys start end)
|
|
226 (setq last-kbd-macro (edmacro-parse-keys (buffer-substring start end)))))
|
|
227
|
|
228 ;;;###autoload
|
134
|
229 (defun kbd (keys)
|
118
|
230 "Convert KEYS to the internal Emacs key representation."
|
134
|
231 (read-kbd-macro keys))
|
118
|
232
|
|
233 ;;;###autoload
|
22
|
234 (defun format-kbd-macro (&optional macro verbose)
|
|
235 "Return the keyboard macro MACRO as a human-readable string.
|
|
236 This string is suitable for passing to `read-kbd-macro'.
|
|
237 Second argument VERBOSE means to put one command per line with comments.
|
|
238 If VERBOSE is `1', put everything on one line. If VERBOSE is omitted
|
|
239 or nil, use a compact 80-column format."
|
|
240 (and macro (symbolp macro) (setq macro (symbol-function macro)))
|
|
241 (edmacro-format-keys (or macro last-kbd-macro) verbose))
|
|
242
|
|
243 ;;; Commands for *Edit Macro* buffer.
|
|
244
|
|
245 (defun edmacro-finish-edit ()
|
|
246 (interactive)
|
|
247 (unless (eq major-mode 'edmacro-mode)
|
|
248 (error
|
|
249 "This command is valid only in buffers created by `edit-kbd-macro'"))
|
|
250 (run-hooks 'edmacro-finish-hook)
|
|
251 (let ((cmd nil) (keys nil) (no-keys nil)
|
|
252 (top (point-min)))
|
|
253 (goto-char top)
|
|
254 (let ((case-fold-search nil))
|
|
255 (while (cond ((looking-at "[ \t]*\\($\\|;;\\|REM[ \t\n]\\)")
|
|
256 t)
|
|
257 ((looking-at "Command:[ \t]*\\([^ \t\n]*\\)[ \t]*$")
|
|
258 (when edmacro-store-hook
|
|
259 (error "\"Command\" line not allowed in this context"))
|
|
260 (let ((str (buffer-substring (match-beginning 1)
|
|
261 (match-end 1))))
|
|
262 (unless (equal str "")
|
|
263 (setq cmd (and (not (equal str "none"))
|
|
264 (intern str)))
|
|
265 (and (fboundp cmd) (not (arrayp (symbol-function cmd)))
|
|
266 (not (y-or-n-p
|
|
267 (format "Command %s is already defined; %s"
|
|
268 cmd "proceed? ")))
|
|
269 (keyboard-quit))))
|
|
270 t)
|
|
271 ((looking-at "Key:\\(.*\\)$")
|
|
272 (when edmacro-store-hook
|
|
273 (error "\"Key\" line not allowed in this context"))
|
|
274 (let ((key (edmacro-parse-keys
|
|
275 (buffer-substring (match-beginning 1)
|
|
276 (match-end 1)))))
|
24
|
277 (unless (equal key [])
|
|
278 (if (equal key [?n ?o ?n ?e])
|
22
|
279 (setq no-keys t)
|
|
280 (push key keys)
|
|
281 (let ((b (key-binding key)))
|
|
282 (and b (commandp b) (not (arrayp b))
|
|
283 (or (not (fboundp b))
|
|
284 (not (arrayp (symbol-function b))))
|
|
285 (not (y-or-n-p
|
|
286 (format "Key %s is already defined; %s"
|
|
287 (edmacro-format-keys key 1)
|
|
288 "proceed? ")))
|
|
289 (keyboard-quit))))))
|
|
290 t)
|
|
291 ((looking-at "Macro:[ \t\n]*")
|
|
292 (goto-char (match-end 0))
|
|
293 nil)
|
|
294 ((eobp) nil)
|
|
295 (t (error "Expected a `Macro:' line")))
|
|
296 (forward-line 1))
|
|
297 (setq top (point)))
|
|
298 (let* ((buf (current-buffer))
|
|
299 (str (buffer-substring top (point-max)))
|
|
300 (modp (buffer-modified-p))
|
|
301 (obuf edmacro-original-buffer)
|
|
302 (store-hook edmacro-store-hook)
|
|
303 (finish-hook edmacro-finish-hook))
|
|
304 (unless (or cmd keys store-hook (equal str ""))
|
|
305 (error "No command name or keys specified"))
|
|
306 (when modp
|
|
307 (when (buffer-name obuf)
|
|
308 (set-buffer obuf))
|
|
309 (message "Compiling keyboard macro...")
|
|
310 (let ((mac (edmacro-parse-keys str)))
|
|
311 (message "Compiling keyboard macro...done")
|
|
312 (if store-hook
|
|
313 (funcall store-hook mac)
|
|
314 (when (eq cmd 'last-kbd-macro)
|
|
315 (setq last-kbd-macro (and (> (length mac) 0) mac))
|
|
316 (setq cmd nil))
|
|
317 (when cmd
|
|
318 (if (= (length mac) 0)
|
|
319 (fmakunbound cmd)
|
|
320 (fset cmd mac)))
|
|
321 (if no-keys
|
|
322 (when cmd
|
24
|
323 (loop for key in (where-is-internal cmd) do
|
22
|
324 (global-unset-key key)))
|
|
325 (when keys
|
|
326 (if (= (length mac) 0)
|
|
327 (loop for key in keys do (global-unset-key key))
|
|
328 (loop for key in keys do
|
|
329 (global-set-key key (or cmd mac)))))))))
|
|
330 (kill-buffer buf)
|
|
331 (when (buffer-name obuf)
|
|
332 (switch-to-buffer obuf))
|
|
333 (when finish-hook
|
|
334 (funcall finish-hook)))))
|
|
335
|
|
336 (defun edmacro-insert-key (key)
|
|
337 "Insert the written name of a key in the buffer."
|
|
338 (interactive "kKey to insert: ")
|
|
339 (if (bolp)
|
|
340 (insert (edmacro-format-keys key t) "\n")
|
|
341 (insert (edmacro-format-keys key) " ")))
|
|
342
|
|
343 (defun edmacro-mode ()
|
|
344 "\\<edmacro-mode-map>Keyboard Macro Editing mode. Press
|
|
345 \\[edmacro-finish-edit] to save and exit.
|
|
346 To abort the edit, just kill this buffer with \\[kill-buffer] RET.
|
|
347
|
|
348 Press \\[edmacro-insert-key] to insert the name of any key by typing the key.
|
|
349
|
|
350 The editing buffer contains a \"Command:\" line and any number of
|
|
351 \"Key:\" lines at the top. These are followed by a \"Macro:\" line
|
|
352 and the macro itself as spelled-out keystrokes: `C-x C-f foo RET'.
|
|
353
|
|
354 The \"Command:\" line specifies the command name to which the macro
|
|
355 is bound, or \"none\" for no command name. Write \"last-kbd-macro\"
|
|
356 to refer to the current keyboard macro (as used by \\[call-last-kbd-macro]).
|
|
357
|
|
358 The \"Key:\" lines specify key sequences to which the macro is bound,
|
|
359 or \"none\" for no key bindings.
|
|
360
|
|
361 You can edit these lines to change the places where the new macro
|
|
362 is stored.
|
|
363
|
|
364
|
|
365 Format of keyboard macros during editing:
|
|
366
|
|
367 Text is divided into \"words\" separated by whitespace. Except for
|
|
368 the words described below, the characters of each word go directly
|
|
369 as characters of the macro. The whitespace that separates words
|
|
370 is ignored. Whitespace in the macro must be written explicitly,
|
|
371 as in \"foo SPC bar RET\".
|
|
372
|
|
373 * The special words RET, SPC, TAB, DEL, LFD, ESC, and NUL represent
|
|
374 special control characters. The words must be written in uppercase.
|
|
375
|
|
376 * A word in angle brackets, e.g., <return>, <down>, or <f1>, represents
|
|
377 a function key. (Note that in the standard configuration, the
|
|
378 function key <return> and the control key RET are synonymous.)
|
|
379 You can use angle brackets on the words RET, SPC, etc., but they
|
|
380 are not required there.
|
|
381
|
|
382 * Keys can be written by their ASCII code, using a backslash followed
|
|
383 by up to six octal digits. This is the only way to represent keys
|
|
384 with codes above \\377.
|
|
385
|
|
386 * One or more prefixes M- (meta), C- (control), S- (shift), A- (alt),
|
|
387 H- (hyper), and s- (super) may precede a character or key notation.
|
|
388 For function keys, the prefixes may go inside or outside of the
|
|
389 brackets: C-<down> = <C-down>. The prefixes may be written in
|
|
390 any order: M-C-x = C-M-x.
|
|
391
|
|
392 Prefixes are not allowed on multi-key words, e.g., C-abc, except
|
|
393 that the Meta prefix is allowed on a sequence of digits and optional
|
|
394 minus sign: M--123 = M-- M-1 M-2 M-3.
|
|
395
|
|
396 * The `^' notation for control characters also works: ^M = C-m.
|
|
397
|
|
398 * Double angle brackets enclose command names: <<next-line>> is
|
|
399 shorthand for M-x next-line RET.
|
|
400
|
|
401 * Finally, REM or ;; causes the rest of the line to be ignored as a
|
|
402 comment.
|
|
403
|
|
404 Any word may be prefixed by a multiplier in the form of a decimal
|
|
405 number and `*': 3*<right> = <right> <right> <right>, and
|
|
406 10*foo = foofoofoofoofoofoofoofoofoofoo.
|
|
407
|
|
408 Multiple text keys can normally be strung together to form a word,
|
|
409 but you may need to add whitespace if the word would look like one
|
|
410 of the above notations: `; ; ;' is a keyboard macro with three
|
|
411 semicolons, but `;;;' is a comment. Likewise, `\\ 1 2 3' is four
|
|
412 keys but `\\123' is a single key written in octal, and `< right >'
|
|
413 is seven keys but `<right>' is a single function key. When in
|
|
414 doubt, use whitespace."
|
|
415 (interactive)
|
|
416 (error "This mode can be enabled only by `edit-kbd-macro'"))
|
|
417 (put 'edmacro-mode 'mode-class 'special)
|
|
418
|
|
419
|
|
420 (defun edmacro-int-char (int)
|
|
421 (if (fboundp 'char-to-int)
|
|
422 (char-to-int int)
|
|
423 int))
|
|
424
|
|
425 ;;; Formatting a keyboard macro as human-readable text.
|
|
426
|
|
427 ;; Changes for XEmacs -- these two functions re-written from scratch.
|
|
428 ;; edmacro-parse-keys always returns a vector. edmacro-format-keys
|
|
429 ;; accepts a vector (but works with a string too). Vector may contain
|
|
430 ;; keypress events. -hniksic
|
|
431 (defun edmacro-parse-keys (string &optional ignored)
|
134
|
432 (let* ((pos 0)
|
|
433 (case-fold-search nil)
|
|
434 (word-to-sym '(("NUL" . (control space))
|
|
435 ("RET" . return)
|
|
436 ("LFD" . linefeed)
|
|
437 ("TAB" . tab)
|
|
438 ("ESC" . escape)
|
|
439 ("SPC" . space)
|
|
440 ("BS" . backspace)
|
|
441 ("DEL" . delete)))
|
|
442 (char-to-word '((?\0 . "NUL")
|
|
443 (?\r . "RET")
|
|
444 (?\n . "LFD")
|
|
445 (?\t . "TAB")
|
|
446 (?\e . "ESC")
|
|
447 (?\ . "SPC")
|
|
448 (?\C-? . "DEL")))
|
|
449 (modifier-prefix-alist '(("C" . control)
|
|
450 ("M" . meta)
|
|
451 ("S" . shift)
|
|
452 ("Sh" . shift)
|
|
453 ("A" . alt)
|
|
454 ("H" . hyper)
|
|
455 ("s" . super)))
|
|
456 ;; string-to-symbol-or-char converter
|
|
457 (conv (lambda (arg)
|
|
458 (if (= (length arg) 1)
|
|
459 (aref arg 0)
|
|
460 (if (string-match "^<\\([^>]+\\)>$" arg)
|
|
461 (setq arg (match-string 1 arg)))
|
|
462 (let ((match (assoc arg word-to-sym)))
|
|
463 (if match
|
|
464 (cdr match)
|
|
465 (intern arg))))))
|
|
466 (conv-chars (lambda (arg)
|
|
467 (let ((match (assoc arg char-to-word)))
|
|
468 (if match
|
|
469 (cdr (assoc (cdr match) word-to-sym))
|
|
470 arg))))
|
|
471 res)
|
22
|
472 (while (and (< pos (length string))
|
|
473 (string-match "[^ \t\n\f]+" string pos))
|
|
474 (let ((word (substring string (match-beginning 0) (match-end 0)))
|
|
475 (times 1)
|
24
|
476 (force-sym nil)
|
134
|
477 (add nil)
|
|
478 match)
|
22
|
479 (setq pos (match-end 0))
|
|
480 (when (string-match "\\([0-9]+\\)\\*." word)
|
|
481 (setq times (string-to-int (substring word 0 (match-end 1))))
|
|
482 (setq word (substring word (1+ (match-end 1)))))
|
24
|
483 (when (string-match "^<\\([^<>]+\\)>$" word)
|
22
|
484 (setq word (match-string 1 word))
|
|
485 (setq force-sym t))
|
|
486 (setq match (assoc word word-to-sym))
|
134
|
487 ;; Add an element; `add' holds the list of elements to be
|
|
488 ;; added.
|
22
|
489 (cond ((string-match "^\\\\[0-7]+" word)
|
|
490 ;; Octal value of character.
|
|
491 (setq add
|
|
492 (list
|
24
|
493 (edmacro-int-char
|
|
494 (edmacro-octal-string-to-integer (substring word 1))))))
|
22
|
495 ((string-match "^<<.+>>$" word)
|
|
496 ;; Extended command.
|
|
497 (setq add
|
|
498 (nconc
|
|
499 (list
|
|
500 (if (eq (key-binding [(meta x)])
|
|
501 'execute-extended-command)
|
|
502 '(meta x)
|
|
503 (or (car (where-is-internal
|
|
504 'execute-extended-command))
|
|
505 '(meta x))))
|
|
506 (mapcar conv-chars (concat (substring word 2 -2) "\r")))
|
24
|
507 ))
|
22
|
508 ((or (equal word "REM") (string-match "^;;" word))
|
134
|
509 ;; Comment (discard to EOL) .
|
22
|
510 (setq pos (string-match "$" string pos)))
|
|
511 (match
|
24
|
512 ;; Convert to symbol.
|
|
513 (setq add (list (cdr match))))
|
22
|
514 ((string-match "^\\^" word)
|
|
515 ;; ^X == C-x
|
|
516 (if (/= (length word) 2)
|
|
517 (error "^ must be followed by one character"))
|
24
|
518 (setq add (list 'control (aref word 0))))
|
118
|
519 ((string-match "^\\([MCSsAH]\\|Sh\\)-" word)
|
|
520 ;; Parse C-* and stuff
|
22
|
521 (setq
|
|
522 add
|
|
523 (list
|
|
524 (let ((pos1 0)
|
|
525 (r1 nil)
|
118
|
526 follow curpart prefix)
|
|
527 (while (progn (setq curpart (substring word pos1))
|
|
528 (string-match "^\\([MCSsAH]\\|Sh\\)-"
|
|
529 curpart))
|
|
530 (setq prefix (assoc (match-string 1 curpart)
|
|
531 modifier-prefix-alist))
|
|
532 (setq r1 (nconc r1 (list (cdr prefix))))
|
|
533 (callf + pos1 (1+ (length (car prefix)))))
|
22
|
534 (setq follow (substring word pos1))
|
|
535 (if (equal follow "")
|
|
536 (error "%s must precede a string"
|
|
537 (substring word 0 pos1)))
|
|
538 (nconc r1 (list (funcall conv follow)))))))
|
|
539 (force-sym
|
|
540 ;; This must be a symbol
|
|
541 (setq add (list (intern word))))
|
|
542 (t
|
|
543 ;; Characters
|
|
544 (setq add (mapcar conv-chars word))))
|
|
545 (let ((new nil))
|
|
546 (loop repeat times do (setq new (append new add)))
|
|
547 (setq add new))
|
|
548 (setq res (nconc res add))))
|
134
|
549 (edmacro-mapvector 'identity res)))
|
22
|
550
|
|
551 (defun edmacro-conv (char-or-sym add-<>)
|
|
552 (let ((char-to-word '((?\0 . "NUL")
|
24
|
553 (?\r . "RET")
|
|
554 (?\n . "LFD")
|
|
555 (?\t . "TAB")
|
|
556 (?\e . "ESC")
|
|
557 (?\ . "SPC")
|
|
558 (?\C-? . "DEL")))
|
22
|
559 (symbol-to-char '((return . ?\r)
|
24
|
560 (linefeed . ?\n)
|
22
|
561 (space . ?\ )
|
|
562 (delete . ?\C-?)
|
|
563 (tab . ?\t)
|
|
564 (escape . ?\e))))
|
|
565 (if (symbolp char-or-sym)
|
|
566 (if (= (length (symbol-name char-or-sym)) 1)
|
|
567 (setq char-or-sym (aref (symbol-name char-or-sym) 0))
|
|
568 (let ((found (assq char-or-sym symbol-to-char)))
|
|
569 (if found
|
|
570 (setq char-or-sym (cdr found))))))
|
|
571 ;; Return:
|
|
572 (cons (symbolp char-or-sym)
|
|
573 (if (symbolp char-or-sym)
|
|
574 (if add-<>
|
|
575 (concat "<" (symbol-name char-or-sym) ">")
|
|
576 (symbol-name char-or-sym))
|
|
577 (let ((found (assq char-or-sym char-to-word)))
|
24
|
578 (cond (found
|
|
579 (cdr found))
|
|
580 ((< char-or-sym 128)
|
|
581 (single-key-description char-or-sym))
|
134
|
582 ((and edmacro-eight-bits
|
|
583 (>= char-or-sym 128))
|
|
584 (char-to-string char-or-sym))
|
24
|
585 (t
|
|
586 (format "\\%o" (edmacro-int-char char-or-sym)))))))))
|
22
|
587
|
|
588 (defun edmacro-format-1 (keys command times togetherp)
|
|
589 (let ((res "")
|
|
590 (start keys)
|
|
591 el)
|
|
592 (while keys
|
|
593 (unless (or (eq start keys) togetherp)
|
|
594 (callf concat res " "))
|
|
595 (if (> times 1)
|
|
596 (setq res (concat (format "%d*" times) res)))
|
|
597 (setq el (car keys))
|
|
598 (callf concat res
|
|
599 (cond ((listp el)
|
|
600 (let ((my ""))
|
|
601 (if (or
|
|
602 (let (cnv)
|
|
603 (while el
|
|
604 (let ((found (assq (car el)
|
|
605 '((control . "C-")
|
|
606 (meta . "M-")
|
|
607 (shift . "S-")
|
|
608 (alt . "A-")
|
|
609 (hyper . "H-")
|
|
610 (super . "s-")))))
|
|
611 (callf concat my
|
|
612 (if found
|
|
613 (cdr found)
|
|
614 (setq cnv (edmacro-conv (car el) nil))
|
|
615 (cdr cnv))))
|
|
616 (setq el (cdr el)))
|
|
617 (car cnv))
|
|
618 (> times 1))
|
|
619 (concat "<" my ">")
|
|
620 my)))
|
|
621 (t
|
|
622 (cdr (edmacro-conv el t)))))
|
|
623 (setq keys (cdr keys)))
|
|
624 (if command
|
|
625 (callf concat res
|
|
626 (concat
|
|
627 (make-string (max (- 3 (/ (length res) tab-width)) 1) ?\t)
|
|
628 ";; "
|
|
629 (symbol-name command)
|
|
630 (if togetherp (format " * %d" (length start))))))
|
|
631 res))
|
|
632
|
24
|
633 ;; Convert the keypress events in vector x to keys, and return a
|
|
634 ;; vector of keys. If a list element is not a keypress event, ignore
|
|
635 ;; it.
|
|
636 (defun edmacro-events-to-keys (x)
|
|
637 (if (or (not (fboundp 'events-to-keys))
|
|
638 (not (arrayp x)))
|
|
639 x
|
|
640 (let ((cnt 0)
|
|
641 (len (length x))
|
|
642 new el)
|
|
643 (while (< cnt len)
|
|
644 (setq el (aref x cnt))
|
|
645 (cond ((eventp el)
|
|
646 (if (mouse-event-p el)
|
|
647 (setq el nil)
|
|
648 (setq el (aref (events-to-keys (vector el)) 0))))
|
|
649 (t
|
|
650 nil)) ; leave it be.
|
|
651 (if el
|
|
652 (setq new (nconc new (list el))))
|
|
653 (incf cnt))
|
134
|
654 (edmacro-mapvector 'identity new))))
|
22
|
655
|
24
|
656 ;; Collapse a list of keys into a list of function keys, where
|
|
657 ;; applicable.
|
|
658 (defun edmacro-fkeys (keys)
|
134
|
659 (let (new k lookup)
|
24
|
660 (while keys
|
|
661 (setq k (nconc k (list (car keys))))
|
134
|
662 (setq lookup (lookup-key function-key-map (edmacro-mapvector 'identity k)))
|
24
|
663 (cond ((vectorp lookup)
|
|
664 (setq new (nconc new (mapcar 'identity lookup)))
|
|
665 (setq k nil))
|
|
666 ((keymapp lookup)
|
|
667 nil)
|
|
668 ((null lookup)
|
|
669 (setq new (nconc new k))
|
|
670 (setq k nil))
|
|
671 (t
|
|
672 (setq k nil)))
|
|
673 (setq keys (cdr keys)))
|
|
674 (if (keymapp lookup)
|
|
675 (setq new (nconc new k)))
|
|
676 new))
|
22
|
677
|
24
|
678 (defun edmacro-format-keys (macro &optional verbose)
|
|
679 ;; XEmacs:
|
|
680 ;; If we're dealing with events, convert them to symbols first.
|
|
681 (setq macro (edmacro-events-to-keys macro))
|
|
682 (if (zerop (length macro))
|
|
683 ""
|
|
684 (let ((res ""))
|
|
685 ;; I'm not sure I understand the original code, but this seems to
|
|
686 ;; work.
|
|
687 (and (eq verbose 1)
|
|
688 (setq verbose nil))
|
|
689
|
134
|
690 ;; We prefer a list -- much easier to process...
|
24
|
691 (setq macro (mapcar 'identity macro))
|
|
692 (setq macro (edmacro-fkeys macro))
|
|
693 (while macro
|
|
694 (let (key lookup (times 1) self-insert-p)
|
|
695 (loop do
|
|
696 (setq key (nconc key (list (car macro)))
|
|
697 macro (cdr macro)
|
134
|
698 lookup (lookup-key global-map (edmacro-mapvector
|
|
699 'identity key)))
|
24
|
700 while
|
134
|
701 (and macro lookup (not (commandp lookup))))
|
|
702 ;; keyboard macro
|
24
|
703 (if (vectorp lookup)
|
|
704 (setq lookup nil))
|
|
705 (if (and (eq lookup 'self-insert-command)
|
|
706 (= (length key) 1)
|
|
707 (not (memq (car key)
|
|
708 '(?\ ?\r ?\n space return linefeed tab))))
|
|
709 (while (and (< (length key) 23)
|
|
710 (eq (lookup-key global-map (car macro))
|
|
711 'self-insert-command)
|
|
712 (not (memq
|
|
713 (car macro)
|
|
714 '(?\ ?\r ?\n space return linefeed tab))))
|
|
715 (setq key (nconc key (list (car macro)))
|
|
716 macro (cdr macro)
|
|
717 self-insert-p t))
|
|
718 (while (edmacro-seq-equal key macro)
|
|
719 (setq macro (nthcdr (length key) macro))
|
|
720 (incf times)))
|
|
721 (if (or self-insert-p
|
|
722 (null (cdr key))
|
|
723 (= times 1))
|
|
724 (callf concat res (edmacro-format-1 key (if verbose lookup
|
|
725 nil)
|
|
726 times self-insert-p)
|
|
727 (and macro (if verbose "\n" " ")))
|
|
728 (loop repeat times
|
|
729 do
|
|
730 (callf concat res
|
|
731 (edmacro-format-1 key (if verbose lookup
|
|
732 nil)
|
|
733 1 self-insert-p)
|
|
734 (and macro (if verbose "\n" " ")))))))
|
|
735 res)))
|
22
|
736
|
|
737 (defun edmacro-seq-equal (seq1 seq2)
|
|
738 (while (and seq1 seq2
|
|
739 (equal (car seq1) (car seq2)))
|
|
740 (setq seq1 (cdr seq1)
|
|
741 seq2 (cdr seq2)))
|
|
742 (not seq1))
|
|
743
|
24
|
744 (defsubst edmacro-oct-char-to-integer (character)
|
|
745 "Take a char and return its value as if it was a octal digit."
|
|
746 (if (and (>= character ?0) (<= character ?7))
|
|
747 (- character ?0)
|
|
748 (error (format "Invalid octal digit `%c'." character))))
|
|
749
|
|
750 (defun edmacro-octal-string-to-integer (octal-string)
|
|
751 "Return decimal integer for OCTAL-STRING."
|
|
752 (interactive "sOctal number: ")
|
|
753 (let ((oct-num 0))
|
|
754 (while (not (equal octal-string ""))
|
|
755 (setq oct-num (+ (* oct-num 8)
|
|
756 (edmacro-oct-char-to-integer
|
|
757 (string-to-char octal-string))))
|
|
758 (setq octal-string (substring octal-string 1)))
|
|
759 oct-num))
|
|
760
|
|
761
|
22
|
762 (defun edmacro-fix-menu-commands (macro)
|
|
763 (when (vectorp macro)
|
|
764 (let ((i 0) ev)
|
|
765 (while (< i (length macro))
|
24
|
766 (when (and (consp (setq ev (aref macro i)))
|
|
767 (not (memq (car ev) ; ha ha
|
134
|
768 '(hyper super meta alt control shift))))
|
22
|
769 (cond ((equal (cadadr ev) '(menu-bar))
|
|
770 (setq macro (vconcat (edmacro-subseq macro 0 i)
|
|
771 (vector 'menu-bar (car ev))
|
|
772 (edmacro-subseq macro (1+ i))))
|
|
773 (incf i))
|
|
774 ;; It would be nice to do pop-up menus, too, but not enough
|
|
775 ;; info is recorded in macros to make this possible.
|
|
776 (t
|
|
777 (error "Macros with mouse clicks are not %s"
|
|
778 "supported by this command"))))
|
|
779 (incf i))))
|
|
780 macro)
|
|
781
|
|
782 ;;; Parsing a human-readable keyboard macro.
|
|
783
|
|
784
|
|
785
|
|
786 ;;; The following probably ought to go in macros.el:
|
|
787
|
|
788 ;;;###autoload
|
|
789 (defun insert-kbd-macro (macroname &optional keys)
|
|
790 "Insert in buffer the definition of kbd macro NAME, as Lisp code.
|
|
791 Optional second arg KEYS means also record the keys it is on
|
|
792 \(this is the prefix argument, when calling interactively).
|
|
793
|
|
794 This Lisp code will, when executed, define the kbd macro with the same
|
|
795 definition it has now. If you say to record the keys, the Lisp code
|
|
796 will also rebind those keys to the macro. Only global key bindings
|
|
797 are recorded since executing this Lisp code always makes global
|
|
798 bindings.
|
|
799
|
|
800 To save a kbd macro, visit a file of Lisp code such as your `~/.emacs',
|
|
801 use this command, and then save the file."
|
|
802 (interactive "CInsert kbd macro (name): \nP")
|
|
803 (let (definition)
|
|
804 (if (string= (symbol-name macroname) "")
|
|
805 (progn
|
|
806 (setq definition (format-kbd-macro))
|
|
807 (insert "(setq last-kbd-macro"))
|
|
808 (setq definition (format-kbd-macro macroname))
|
|
809 (insert (format "(defalias '%s" macroname)))
|
|
810 (if (> (length definition) 50)
|
|
811 (insert " (read-kbd-macro\n")
|
|
812 (insert "\n (read-kbd-macro "))
|
|
813 (prin1 definition (current-buffer))
|
|
814 (insert "))\n")
|
|
815 (if keys
|
24
|
816 (let ((keys (where-is-internal macroname)))
|
22
|
817 (while keys
|
|
818 (insert (format "(global-set-key %S '%s)\n" (car keys) macroname))
|
|
819 (setq keys (cdr keys)))))))
|
|
820
|
|
821 (provide 'edmacro)
|
|
822
|
|
823 ;;; edmacro.el ends here
|