# HG changeset patch # User Kim F. Storm # Date 1025303838 0 # Node ID bd954daa704a880ceab7196cda77aa012e8385ca # Parent 6303529069a93c1d809f68b83b14311f2b6973bf New file. diff -r 6303529069a9 -r bd954daa704a lisp/kmacro.el --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lisp/kmacro.el Fri Jun 28 22:37:18 2002 +0000 @@ -0,0 +1,358 @@ +;;; kmacro.el --- enhanced keyboard macros + +;; Copyright (C) 1996-2002 Free Software Foundation, Inc. + +;; Author: Kim F. Storm +;; Based on: iswitchb by Stephen Eglen +;; 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 "") + (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))) +