changeset 52223:d37e0b4125e8

(kmacro-keymap): Group related bindings in initialization for clarity. Bind C-s to start macro. Remove C-r binding. (kmacro-initial-counter-value): New defvar to hold initial counter value in case we set the value before defining a macro. (kmacro-insert-counter): Clear kmacro-initial-counter-value.. (kmacro-set-counter): Set kmacro-initial-counter-value if we are not defining or executing macro. Doc fix. (kmacro-add-counter): Clear kmacro-initial-counter-value. (kmacro-view-last-item, kmacro-view-item-no): New defvars used to temporarily view older elements on the macro ring without cycling the ring. (kmacro-display): Doc fix. (kmacro-exec-ring-item): New helper function. (kmacro-call-ring-2nd): Use it. (kmacro-call-ring-2nd-repeat): Doc fix. (kmacro-start-macro): Use (and clear) kmacro-initial-counter-value. (kmacro-end-or-call-macro): Execute last viewed macro (using kmacro-exec-ring-item) from ring if this follows kmacro-view-macro. This allows us to find a macro on the ring with C-x C-k C-v C-v ... and execute it (with C-k) without cycling the ring to bring it to the head of the ring. (kmacro-bind-to-key): Doc fix (describe reserved bindings). Allow binding to reserved keys without specifying C-x C-k prefix. Ask for confirmation if entered key sequence is already bound to a non-macro command. (kmacro-view-macro): Repeating command will show older elements on the macro ring; C-k will execute the last viewed macro. (kmacro-view-macro-repeat): Doc fix. Change its kmacro-repeat property from 'ring to 'head.
author Kim F. Storm <storm@cua.dk>
date Sun, 17 Aug 2003 22:04:44 +0000
parents 2573b58491df
children 57534ab575bd
files lisp/kmacro.el
diffstat 1 files changed, 139 insertions(+), 38 deletions(-) [+]
line wrap: on
line diff
--- a/lisp/kmacro.el	Sun Aug 17 22:04:05 2003 +0000
+++ b/lisp/kmacro.el	Sun Aug 17 22:04:44 2003 +0000
@@ -174,29 +174,37 @@
 
 (defvar kmacro-keymap
   (let ((map (make-sparse-keymap)))
+    ;; Start, end, execute macros
     (define-key map "s"    'kmacro-start-macro)
+    (define-key map "\C-s" 'kmacro-start-macro)
     (define-key map "\C-k" 'kmacro-end-or-call-macro-repeat)
-    (define-key map "\C-e" 'kmacro-edit-macro-repeat)
-    (define-key map "\r"   'kmacro-edit-macro)
-    (define-key map " "    'kmacro-step-edit-macro)
-    (define-key map "l"    'kmacro-edit-lossage)
+    (define-key map "r"    'apply-macro-to-region-lines)
+    (define-key map "q"    'kbd-macro-query)  ;; Like C-x q
+
+    ;; macro ring
+    (define-key map "\C-n" 'kmacro-cycle-ring-next)
+    (define-key map "\C-p" 'kmacro-cycle-ring-previous)
+    (define-key map "\C-v" 'kmacro-view-macro-repeat)
+    (define-key map "\C-d" 'kmacro-delete-ring-head)
+    (define-key map "\C-t" 'kmacro-swap-ring)
+    (define-key map "\C-l" 'kmacro-call-ring-2nd-repeat)
+
+    ;; macro counter
+    (define-key map "\C-f" 'kmacro-set-format)
+    (define-key map "\C-c" 'kmacro-set-counter)
     (define-key map "\C-i" 'kmacro-insert-counter)
     (define-key map "\C-a" 'kmacro-add-counter)
-    (define-key map "\C-v" 'kmacro-view-macro-repeat)
-    (define-key map "\C-l" 'kmacro-call-ring-2nd-repeat)
-    (define-key map "\C-r" 'kmacro-view-ring-2nd)
-    (define-key map "\C-n" 'kmacro-cycle-ring-next)
-    (define-key map "\C-p" 'kmacro-cycle-ring-previous)
-    (define-key map "\C-f" 'kmacro-set-format)
-    (define-key map "\C-c" 'kmacro-set-counter)
-    (define-key map "\C-t" 'kmacro-swap-ring)
+
+    ;; macro editing
+    (define-key map "\C-e" 'kmacro-edit-macro-repeat)
+    (define-key map "\r"   'kmacro-edit-macro)
+    (define-key map "e"    'edit-kbd-macro)
+    (define-key map "l"    'kmacro-edit-lossage)
+    (define-key map " "    'kmacro-step-edit-macro)
+
+    ;; naming and binding
     (define-key map "b"    'kmacro-bind-to-key)
-    (define-key map "\C-d" 'kmacro-delete-ring-head)
-    ;; Compatibility bindings
-    (define-key map "q"    'kbd-macro-query)
     (define-key map "n"    'name-last-kbd-macro)
-    (define-key map "e"    'edit-kbd-macro)
-    (define-key map "r"    'apply-macro-to-region-lines)
     map)
   "Keymap for keyboard macro commands.")
 (defalias 'kmacro-keymap kmacro-keymap)
@@ -229,13 +237,18 @@
 (defvar kmacro-counter-value-start kmacro-counter
   "Macro counter at start of macro execution.")
 
-(defvar kmacro-last-counter 0 "Last counter inserted by key macro.")
+(defvar kmacro-last-counter 0
+  "Last counter inserted by key macro.")
+
+(defvar kmacro-initial-counter-value nil
+  "Initial counter value for the next keyboard macro to be defined.")
 
 
 (defun kmacro-insert-counter (arg)
   "Insert macro counter and increment with ARG or 1 if missing.
 With \\[universal-argument], insert previous kmacro-counter (but do not modify counter)."
   (interactive "P")
+  (setq kmacro-initial-counter-value nil)
   (if (and arg (listp arg))
       (insert (format kmacro-counter-format kmacro-last-counter))
     (insert (format kmacro-counter-format kmacro-counter))
@@ -260,12 +273,16 @@
 
 (defun kmacro-set-counter (arg)
   "Set kmacro-counter to ARG or prompt if missing.
-With \\[universal-argument], reset counter to its value prior to this iteration of the macro."
+With \\[universal-argument] prefix, reset counter to its value prior to this iteration of the macro."
   (interactive "NMacro counter value: ")
   (setq kmacro-last-counter kmacro-counter
 	kmacro-counter (if (and current-prefix-arg (listp current-prefix-arg))
 			   kmacro-counter-value-start
 			 arg))
+  ;; setup initial macro counter value if we are not executing a macro.
+  (setq kmacro-initial-counter-value
+	(and (not (or defining-kbd-macro executing-kbd-macro))
+	     kmacro-counter))
   (unless executing-kbd-macro
     (kmacro-display-counter)))
 
@@ -274,6 +291,7 @@
   "Add numeric prefix arg (prompt if missing) to macro counter.
 With \\[universal-argument], restore previous counter value."
   (interactive "NAdd to macro counter: ")
+  (setq kmacro-initial-counter-value nil)
   (let ((last kmacro-last-counter))
     (setq kmacro-last-counter kmacro-counter
 	  kmacro-counter (if (and current-prefix-arg (listp current-prefix-arg))
@@ -303,6 +321,11 @@
 instead it is available in the variables `last-kbd-macro', `kmacro-counter',
 and `kmacro-counter-format'.")
 
+;; Remember what we are currently looking at with kmacro-view-macro.
+
+(defvar kmacro-view-last-item nil)
+(defvar kmacro-view-item-no 0)
+
 
 (defun kmacro-ring-head ()
   "Return pseudo head element in macro ring."
@@ -361,8 +384,11 @@
    (t nil)))
 
 
-(defun kmacro-display (macro &optional trunc descr empty )
-  "Display a keyboard MACRO."
+(defun kmacro-display (macro &optional trunc descr empty)
+  "Display a keyboard MACRO.
+Optional arg TRUNC non-nil specifies to limit width of macro to 60 chars.
+Optional arg DESCR is descriptive text for macro; default is \"Macro:\".
+Optional arg EMPTY is message to print if no macros are defined."
   (if macro
       (let* ((x 60)
 	     (m (format-kbd-macro macro))
@@ -410,19 +436,26 @@
 	 keys)))
 
 
+(defun kmacro-exec-ring-item (item arg)
+  "Execute item ITEM from the macro ring."
+  ;; Use counter and format specific to the macro on the ring!
+  (let ((kmacro-counter (nth 1 item))
+	(kmacro-counter-format-start (nth 2 item)))
+    (execute-kbd-macro (car item) arg #'kmacro-loop-setup-function)
+    (setcar (cdr item) kmacro-counter)))
+
+
 (defun kmacro-call-ring-2nd (arg)
   "Execute second keyboard macro at in macro ring."
   (interactive "P")
   (unless (kmacro-ring-empty-p)
-    ;; should use counter format specific to the macro on the ring!
-    (let ((kmacro-counter (nth 1 (car kmacro-ring)))
-	  (kmacro-counter-format-start (nth 2 (car kmacro-ring))))
-      (execute-kbd-macro (car (car kmacro-ring)) arg #'kmacro-loop-setup-function)
-      (setcar (cdr (car kmacro-ring)) kmacro-counter))))
+    (kmacro-exec-ring-item (car kmacro-ring) arg)))
 
 
 (defun kmacro-call-ring-2nd-repeat (arg)
-  "Like `kmacro-call-ring-2nd', but allow repeat without repeating prefix."
+  "Execute second keyboard macro at in macro ring.
+This is like `kmacro-call-ring-2nd', but allows repeating macro commands
+without repeating the prefix."
   (interactive "P")
   (let ((keys (kmacro-get-repeat-prefix)))
     (kmacro-call-ring-2nd arg)
@@ -439,7 +472,6 @@
     (kmacro-display (car (car kmacro-ring)) "2nd macro")))
 
 
-
 (defun kmacro-cycle-ring-next (&optional arg)
   "Move to next keyboard macro in keyboard macro ring.
 Displays the selected macro in the echo area."
@@ -533,7 +565,10 @@
 		     kmacro-ring))
 	      (if (>= len kmacro-ring-max)
 		  (setcdr (nthcdr len kmacro-ring) nil))))
-	(setq kmacro-counter (if arg (prefix-numeric-value arg) 0)
+	(setq kmacro-counter (or (if arg (prefix-numeric-value arg))
+				 kmacro-initial-counter-value
+				 0)
+	      kmacro-initial-counter-value nil
 	      kmacro-counter-value-start kmacro-counter
 	      kmacro-last-counter kmacro-counter
 	      kmacro-counter-format-start kmacro-counter-format))
@@ -645,6 +680,9 @@
      (if kmacro-call-repeat-key
 	 (kmacro-call-macro arg no-repeat t)
        (kmacro-end-macro arg)))
+   ((and (eq this-command 'kmacro-view-macro)  ;; We are in repeat mode!
+	 kmacro-view-last-item)
+    (kmacro-exec-ring-item (car kmacro-view-last-item) arg))
    ((and arg (listp arg))
     (kmacro-call-ring-2nd 1))
    (t
@@ -689,34 +727,97 @@
 
 ;;; Misc. commands
 
+;; An idea for macro bindings:
+;; Create a separate keymap installed as a minor-mode keymap (e.g. in
+;; the emulation-mode-map-alists) in which macro bindings are made
+;; independent of any other bindings.  When first binding is made,
+;; the kemap is created, installed, and enabled.  Key seq. C-x C-k +
+;; can then be used to toggle the use of this keymap on and off.
+;; This means that it would be safe(r) to bind ordinary keys like
+;; letters and digits, provided that we inhibit the keymap while
+;; executing the macro later on (but that's controversial...)
+
 (defun kmacro-bind-to-key (arg)
-  "When not defining or executing a macro, offer to bind last macro to a key."
+  "When not defining or executing a macro, offer to bind last macro to a key.
+The key sequences [C-x C-k 0] through [C-x C-k 9] and [C-x C-k A]
+through [C-x C-k Z] are reserved for user bindings, and to bind to
+one of these sequences, just enter the digit or letter, rather than
+the whole sequence.
+
+You can bind to any valid key sequence, but if you try to bind to
+a key with an existing command binding, you will be asked for
+confirmation whether to replace that binding.  Note that the
+binding is made in the `global-map' keymap, so the macro binding
+may be shaded by a local key binding."
   (interactive "p")
   (if (or defining-kbd-macro executing-kbd-macro)
       (if defining-kbd-macro
 	  (message "Cannot save macro while defining it."))
     (unless last-kbd-macro
       (error "No keyboard macro defined"))
-    (let ((key-seq (read-key-sequence "Bind last macro to key: ")))
-      (unless (equal key-seq "")
-	(define-key global-map key-seq last-kbd-macro)))))
+    (let ((key-seq (read-key-sequence "Bind last macro to key: "))
+	  ok cmd)
+      (when (= (length key-seq) 1)
+	(let ((ch (aref key-seq 0)))
+	  (if (or (and (>= ch ?0) (<= ch ?9))
+		  (and (>= ch ?A) (<= ch ?Z)))
+	      (setq key-seq (concat "\C-x\C-k" key-seq)
+		    ok t))))
+      (when (and (not (equal key-seq ""))
+		 (or ok
+		     (not (setq cmd (key-binding key-seq)))
+		     (stringp cmd)
+		     (vectorp cmd)
+		     (yes-or-no-p (format "%s runs command %S.  Bind anyway? "
+					  (format-kbd-macro key-seq)
+					  cmd))))
+	(define-key global-map key-seq last-kbd-macro)
+	(message "Keyboard macro bound to %s" (format-kbd-macro key-seq))))))
 
 
 (defun kmacro-view-macro (&optional arg)
-  "Display the last keyboard macro."
+  "Display the last keyboard macro.
+If repeated, it shows previous elements in the macro ring."
   (interactive)
-  (kmacro-display last-kbd-macro))
-
+  (cond
+   ((or (kmacro-ring-empty-p)
+	(not (eq last-command 'kmacro-view-macro)))
+    (setq kmacro-view-last-item nil))
+   ((null kmacro-view-last-item)
+    (setq kmacro-view-last-item kmacro-ring
+	  kmacro-view-item-no 2))
+   ((consp kmacro-view-last-item)
+    (setq kmacro-view-last-item (cdr kmacro-view-last-item)
+	  kmacro-view-item-no (1+ kmacro-view-item-no)))
+   (t
+    (setq kmacro-view-last-item nil)))
+  (setq this-command 'kmacro-view-macro
+	last-command this-command) ;; in case we repeat
+  (kmacro-display (if kmacro-view-last-item
+		      (car (car kmacro-view-last-item))
+		    last-kbd-macro)
+		  nil
+		  (if kmacro-view-last-item
+		      (concat (cond ((= kmacro-view-item-no 2) "2nd")
+				    ((= kmacro-view-item-no 3) "3nd")
+				    (t (format "%dth" kmacro-view-item-no)))
+			      " previous macro")
+		    "Last macro")))
 
 (defun kmacro-view-macro-repeat (&optional arg)
-  "Like `kmacro-view-macro', but allow repeat without repeating prefix."
+  "Display the last keyboard macro.
+If repeated, it shows previous elements in the macro ring.
+To execute the displayed macro ring item without changing the macro ring,
+just enter C-k.
+This is like `kmacro-view-macro', but allows repeating macro commands
+without repeating the prefix."
   (interactive)
   (let ((keys (kmacro-get-repeat-prefix)))
     (kmacro-view-macro arg)
     (if (and last-kbd-macro keys)
 	(kmacro-repeat-on-last-key keys))))
 
-(put 'kmacro-view-macro-repeat 'kmacro-repeat 'head)
+(put 'kmacro-view-macro-repeat 'kmacro-repeat 'ring)
 
 
 (defun kmacro-edit-macro-repeat (&optional arg)