--- /dev/null
+;;; kmacro.el --- enhanced keyboard macros
+
+;; Copyright (C) 1996-2002 Free Software Foundation, Inc.
+
+;; Author: Kim F. Storm <storm@cua.dk>
+;; Based on: iswitchb by Stephen Eglen <stephen@cns.ed.ac.uk>
+;; Keywords: extensions convenience
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 2, or (at your option)
+;; any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs; see the file COPYING. If not, write to the
+;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
+;;; Commentary:
+
+;; The kmacro package is an alternative user interface to emacs'
+;; keyboard macro functionality. This functionality is normally bound
+;; to C-x (, C-x ), and C-x C-e, but these bindings are too hard to
+;; type to be really useful for doing small repeated tasks.
+
+;; With kmacro, two function keys are dedicated to keyboard macros,
+;; by default F7 and F8. Personally, I prefer F1 and F2, but those
+;; keys already have default bindings.
+;;
+;; To start defining a keyboard macro, use F7. To end the macro,
+;; use F8, and to call the macro also use F8. This makes it very
+;; easy to repeat a macro immediately after defining it.
+;;
+;; You can call the macro repeatedly by pressing F8 multiple times, or
+;; you can give a numeric prefix argument specifying the number of
+;; times to repeat the macro. Macro execution automatically
+;; terminates when point reaches the end of the buffer or if an error
+;; is signalled by ringing the bell.
+
+;; If you enter F7 while defining the macro, the numeric value of
+;; `kmacro-counter' is inserted using the `kmacro-counter-format', and
+;; `kmacro-counter' is incremented by 1 (or the numeric prefix value
+;; of F7).
+;;
+;; The initial value of `kmacro-counter' is 0, or the numeric prefix
+;; value given to F7 when starting the macro.
+;;
+;; Now, each time you call the macro using F8, the current
+;; value of `kmacro-counter' is inserted and incremented, making it
+;; easy to insert incremental numbers in the buffer.
+;;
+;; Example:
+;;
+;; The following sequence: M-5 F7 x M-2 F7 y F8 F8 F8 F8
+;; inserts the following string: x5yx7yx9yx11y
+
+;; A macro can also be call using a mouse click, default S-mouse-3.
+;; This calls the macro at the point where you click the mouse.
+
+;; When you have defined another macro, which is thus called via F8,
+;; the previous macro is pushed onto a keyboard macro ring. The head
+;; macro on the ring can be executed using S-F8. You can cycle the
+;; macro ring using C-F8. You can also swap the last macro and the
+;; head of the macro ring using C-u F8.
+
+;; You can edit the last macro using M-F7.
+
+;; You can append to the last macro using C-u F7.
+
+;; You can set the macro counter using C-F7, and you can set
+;; the macro counter format with S-F7..
+
+;; The following key bindings are performed:
+;;
+;; Normal While defining macro
+;; --------------------------- ------------------------------
+;; f7 Define macro Insert current counter value
+;; Prefix arg specifies initial and increase counter by prefix
+;; counter value (default 0) (default increment: 1)
+;;
+;; C-u f7 APPENDs to last macro
+;;
+;; f8 Call last macro End macro
+;; Prefix arg specifies number
+;; of times to execute macro.
+;;
+;; C-u f8 Swap last and head of macro ring.
+;;
+;; S-f7 Set the format of the macro Ditto, but notice that the
+;; counter (default: %d). format is reset at the next
+;; invocation of the macro.
+;;
+;; C-f7 Set the macro counter value Increase/decrease counter value
+;; to the prefix value. by the prefix value, or if prefix
+;; is C-u, set counter to 0.
+;;
+;; M-f7 Edit the last macro.
+;;
+;; S-f8 Call the previous macro.
+;;
+;; C-f8 Cycle the macro ring.
+;;
+;; S-mouse-3 Set point at click and End macro and execute macro at
+;; execute last macro. click.
+
+;;; Code:
+
+(provide 'kmacro)
+
+;;; Customization:
+
+(defgroup kmacro nil
+ "Simplified keyboard macro user interface."
+ :group 'keyboard
+ :group 'convenience
+ :link '(emacs-commentary-link :tag "Commentary" "kmacro.el")
+ :link '(emacs-library-link :tag "Lisp File" "kmacro.el"))
+
+;;;###autoload
+(defcustom kmacro-initialize nil
+ "Setting this variable turns on the kmacro functionality.
+This binds the kmacro function keys in the global-map, so
+unsetting this variable does not have any effect!"
+ :set #'(lambda (symbol value)
+ (if value (kmacro-initialize))
+ (set symbol value))
+ :initialize 'custom-initialize-default
+ :require 'kmacro
+ :link '(emacs-commentary-link "kmacro.el")
+ :set-after '(kmacro-start-key kmacro-call-key kmacro-mouse-button)
+ :version "21.4"
+ :type 'boolean
+ :group 'kmacro)
+
+(defcustom kmacro-start-key 'f7
+ "The function key used by kmacro to start a macro."
+ :type 'symbol
+ :group 'kmacro)
+
+(defcustom kmacro-call-key 'f8
+ "The function key used by kmacro to end and call a macro."
+ :type 'symbol
+ :group 'kmacro)
+
+(defcustom kmacro-call-mouse-event 'S-mouse-3
+ "The mouse event used by kmacro to call a macro."
+ :type 'symbol
+ :group 'kmacro)
+
+;; State variables
+
+(defvar kmacro-counter 0
+ "*Current keyboard macro counter")
+
+(defvar kmacro-counter-format "%d"
+ "*Current keyboard macro counter format")
+
+(defvar kmacro-counter-format-start kmacro-counter-format
+ "Macro format at start of macro execution.")
+
+(defvar kmacro-last-counter 0 "Last counter inserted by key macro")
+(defvar kmacro-append-to nil "Last key macro if appending to macro")
+(defvar kmacro-ring nil "Key macro ring")
+
+(defvar kmacro-ring-max 4
+ "*Maximum number of key macros to save in key macro ring")
+
+(defun kmacro-display (macro)
+ "Display a keyboard macro."
+ (let (s)
+ (if (stringp macro)
+ (setq s (if (> (length macro) 50)
+ (concat (substring macro 0 50) "...")
+ macro))
+ (if (vectorp macro)
+ (let (v (i 0) (n (length macro)))
+ (setq s "")
+ (while (and (< i n) (< (length s) 50))
+ (setq v (aref macro i))
+ (setq s (cond
+ ((numberp v) (concat s (char-to-string v)))
+ ((stringp v) (concat s v))
+ ((symbolp v) (concat s "[" (symbol-name v) "]"))
+ (t s)))
+ (setq i (1+ i)))
+ (if (< i n)
+ (setq s (concat s "..."))))))
+ (message (format "Macro: %s" s))))
+
+
+(defun kmacro-start-macro (arg)
+ "Set kmacro-counter to ARG or 0 if missing, and start-kbd-macro.
+With \\[universal-argument], append to current keyboard macro (keep kmacro-counter).
+
+When defining/executing macro, insert macro counter and increment with
+ARG or 1 if missing.
+With \\[universal-argument], insert previous kmacro-counter (but do not modify counter).
+
+The macro counter can be modified via \\[kmacro-set-counter].
+The format of the counter can be modified via \\[kmacro-set-format]."
+ (interactive "p")
+ (if (or defining-kbd-macro executing-kbd-macro)
+ (if (and current-prefix-arg (listp current-prefix-arg))
+ (insert (format kmacro-counter-format kmacro-last-counter))
+ (insert (format kmacro-counter-format kmacro-counter))
+ (setq kmacro-last-counter kmacro-counter
+ kmacro-counter (+ kmacro-counter arg)))
+ (if (and current-prefix-arg (listp current-prefix-arg))
+ (setq kmacro-append-to last-kbd-macro)
+ (setq kmacro-append-to nil
+ kmacro-counter (if current-prefix-arg arg 0)
+ kmacro-last-counter kmacro-counter))
+ (if last-kbd-macro
+ (let ((len (length kmacro-ring)))
+ (setq kmacro-ring (cons last-kbd-macro kmacro-ring))
+ (if (>= len kmacro-ring-max)
+ (setcdr (nthcdr len kmacro-ring) nil))))
+ (setq kmacro-counter-format-start kmacro-counter-format)
+ (start-kbd-macro nil)
+ (if kmacro-append-to (message "Appending to keyboard macro..."))
+))
+
+(defun kmacro-call-macro (arg)
+ "End kbd macro if currently being defined; else call last kbd macro.
+With numeric prefix argument, repeat macro that many times.
+With \\[universal-argument], swap current macro with head of macro ring."
+ (interactive "p")
+ (cond
+ (defining-kbd-macro
+ (end-kbd-macro)
+ (if kmacro-append-to
+ (setq last-kbd-macro (concat kmacro-append-to last-kbd-macro)
+ kmacro-append-to nil)))
+ ((and current-prefix-arg (listp current-prefix-arg))
+ (when kmacro-ring
+ (let ((head (car kmacro-ring)))
+ (setq kmacro-ring (cons last-kbd-macro (cdr kmacro-ring)))
+ (setq last-kbd-macro head)))
+ (kmacro-display last-kbd-macro))
+ (t
+ (setq kmacro-counter-format kmacro-counter-format-start)
+ (call-last-kbd-macro arg))))
+
+(defun kmacro-call-macro-ring (arg)
+ "End kbd macro if currently being defined; else call last kbd macro.
+With \\[universal-argument], display current macro."
+ (interactive "p")
+ (if kmacro-ring
+ (execute-kbd-macro (car kmacro-ring) arg)))
+
+(defun kmacro-end-call-mouse (event)
+ "Move point to the position clicked with the mouse and call last kbd macro.
+If kbd macro currently being defined end it before activating it."
+ (interactive "e")
+ (when defining-kbd-macro
+ (end-kbd-macro)
+ (if kmacro-append-to
+ (setq last-kbd-macro (concat kmacro-append-to last-kbd-macro)
+ kmacro-append-to nil)))
+ (mouse-set-point event)
+ (call-last-kbd-macro nil))
+
+(defun kmacro-cycle-macro-ring (&optional previous)
+ "Cycle the keyboard macro ring on \\[kmacro-call-macro-ring].
+Moves to the next element in the keyboard macro ring.
+With \\[universal-argument] prefix, move to the previous element in the ring.
+Displays the selected macro in the echo area."
+ (interactive "p")
+ (if (null kmacro-ring)
+ (message "No keymacros in ring")
+ (cond
+ ((not (eq this-command last-command))
+ nil)
+ ((= (length kmacro-ring) 1)
+ nil)
+ (previous
+ (let* ((len (length kmacro-ring))
+ (tail (nthcdr (- len 2) kmacro-ring))
+ (elt (car (cdr tail))))
+ (setcdr tail nil)
+ (setq kmacro-ring (cons elt kmacro-ring))))
+ (t
+ (let ((elt (car kmacro-ring)))
+ (setq kmacro-ring (cdr kmacro-ring))
+ (nconc kmacro-ring (list elt)))))
+ (kmacro-display (car kmacro-ring))))
+
+(defun kmacro-save-macro-on-key (arg)
+ "When not defining or executing a macro, offer to save last macro on a key."
+ (interactive "p")
+ (if (or defining-kbd-macro executing-kbd-macro)
+ nil
+ (or last-kbd-macro
+ (error "No keyboard macro defined"))
+ (let ((key-seq (read-key-sequence "Save last macro on key: ")))
+ (or (equal key-seq "\a")
+ (define-key global-map key-seq last-kbd-macro))))
+)
+
+(defun kmacro-set-counter (arg)
+ "Set kmacro-counter to ARG or 0 if missing.
+While defining/executing key macro, increase or decrease counter.
+With \\[universal-argument], unconditionally set counter to 0."
+ (interactive "p")
+ (setq kmacro-counter
+ (cond ((and current-prefix-arg (listp current-prefix-arg)) 0)
+ ((or defining-kbd-macro executing-kbd-macro) (+ kmacro-counter arg))
+ (current-prefix-arg arg)
+ (t 0))))
+
+(defun kmacro-set-format (format)
+ "Set macro counter format"
+ (interactive "sMacro Counter Format (printf format): ")
+ (setq kmacro-counter-format
+ (if (equal format "")
+ "%d"
+ format))
+
+ ;; redefine initial macro counter if we are not executing a macro.
+ (if (not (or defining-kbd-macro executing-kbd-macro))
+ (setq kmacro-counter-format-start kmacro-counter-format))
+)
+
+(defun kmacro-edit-macro ()
+ "Edit keyboard macro."
+ (interactive)
+ (edit-kbd-macro "\r"))
+
+;;;###autoload
+(defun kmacro-initialize (&optional start-key call-key call-mouse)
+ "Setup key bindings for the keyboard macro package.
+If specified, use keys START-KEY, CALL-KEY, and CALL-MOUSE.
+Don't bind to any mouse event if CALL-MOUSE is t.
+Otherwise, use customized keys."
+
+ (setq start-key (or start-key kmacro-start-key 'f7))
+ (setq call-key (or call-key kmacro-call-key 'f8))
+ (setq call-mouse (or call-mouse kmacro-call-mouse-event 'S-mouse-3))
+
+ (global-set-key (vector start-key) 'kmacro-start-macro)
+ (global-set-key (vector (list 'shift start-key)) 'kmacro-set-format)
+ (global-set-key (vector (list 'control start-key)) 'kmacro-set-counter)
+ (global-set-key (vector (list 'meta start-key)) 'kmacro-edit-macro)
+
+ (global-set-key (vector call-key) 'kmacro-call-macro)
+ (global-set-key (vector (list 'shift call-key)) 'kmacro-call-macro-ring)
+ (global-set-key (vector (list 'control call-key)) 'kmacro-cycle-macro-ring)
+
+ (unless (eq call-mouse t)
+ (global-set-key (vector call-mouse) 'kmacro-end-call-mouse)))
+