summaryrefslogtreecommitdiff
path: root/lisp/edmacro.el
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/edmacro.el')
-rw-r--r--lisp/edmacro.el143
1 files changed, 116 insertions, 27 deletions
diff --git a/lisp/edmacro.el b/lisp/edmacro.el
index e87d271ecb2..abfc380d154 100644
--- a/lisp/edmacro.el
+++ b/lisp/edmacro.el
@@ -73,9 +73,19 @@ Default nil means to write characters above \\177 in octal notation."
:type 'boolean
:group 'kmacro)
+(defcustom edmacro-reverse-macro-lines nil
+ "If non-nil, `edit-kbd-macro' shows most recent line of key sequences first.
+
+This is useful when dealing with long lists of key sequences, such as
+from `kmacro-edit-lossage'."
+ :type 'boolean
+ :group 'kmacro
+ :version "30.1")
+
(defvar-keymap edmacro-mode-map
"C-c C-c" #'edmacro-finish-edit
- "C-c C-q" #'edmacro-insert-key)
+ "C-c C-q" #'edmacro-insert-key
+ "C-c C-r" #'edmacro-set-macro-to-region-lines)
(defface edmacro-label
'((default :inherit bold)
@@ -88,20 +98,23 @@ Default nil means to write characters above \\177 in octal notation."
:group 'kmacro)
(defvar edmacro-mode-font-lock-keywords
- `((,(rx bol (group (or "Command" "Key" "Macro") ":")) 0 'edmacro-label)
+ `((,(rx bol (group (or "Command" "Key"
+ (seq "Macro" (zero-or-one " (most recent line first)")))
+ ":"))
+ 0 'edmacro-label)
(,(rx bol
(group ";; Keyboard Macro Editor. Press ")
- (group (*? any))
+ (group (*? nonl))
(group " to finish; press "))
(1 'font-lock-comment-face)
(2 'help-key-binding)
(3 'font-lock-comment-face)
- (,(rx (group (*? any))
- (group " to cancel" (* any)))
+ (,(rx (group (*? nonl))
+ (group " to cancel" (* nonl)))
nil nil
(1 'help-key-binding)
(2 'font-lock-comment-face)))
- (,(rx (one-or-more ";") (zero-or-more any)) 0 'font-lock-comment-face)))
+ (,(rx (one-or-more ";") (zero-or-more nonl)) 0 'font-lock-comment-face)))
(defvar edmacro-store-hook)
(defvar edmacro-finish-hook)
@@ -111,9 +124,9 @@ Default nil means to write characters above \\177 in octal notation."
(defun edit-kbd-macro (keys &optional prefix finish-hook store-hook)
"Edit a keyboard macro.
At the prompt, type any key sequence which is bound to a keyboard macro.
-Or, type `\\[kmacro-end-and-call-macro]' or \\`RET' to edit the last
-keyboard macro, `\\[view-lossage]' to edit the last 300
-keystrokes as a keyboard macro, or `\\[execute-extended-command]'
+Or, type \\[kmacro-end-and-call-macro] or \\`RET' to edit the last
+keyboard macro, \\[view-lossage] to edit the last 300
+keystrokes as a keyboard macro, or \\[execute-extended-command]
to edit a macro by its command name.
With a prefix argument, format the macro in a more concise way."
(interactive
@@ -166,7 +179,13 @@ With a prefix argument, format the macro in a more concise way."
(let* ((oldbuf (current-buffer))
(mmac (edmacro-fix-menu-commands mac))
(fmt (edmacro-format-keys mmac 1))
- (fmtv (edmacro-format-keys mmac (not prefix)))
+ (fmtv (let ((fmtv (edmacro-format-keys mmac (not prefix))))
+ (if (not edmacro-reverse-macro-lines)
+ fmtv
+ (with-temp-buffer
+ (insert fmtv)
+ (reverse-region (point-min) (point-max))
+ (buffer-string)))))
(buf (get-buffer-create "*Edit Macro*")))
(message "Formatting keyboard macro...done")
(switch-to-buffer buf)
@@ -181,6 +200,9 @@ With a prefix argument, format the macro in a more concise way."
(setq-local font-lock-defaults
'(edmacro-mode-font-lock-keywords nil nil ((?\" . "w"))))
(setq font-lock-multiline nil)
+ ;; Make buffer-local so that the commands still work
+ ;; even if the default value changes.
+ (make-local-variable 'edmacro-reverse-macro-lines)
(erase-buffer)
(insert (substitute-command-keys
(concat
@@ -202,7 +224,9 @@ With a prefix argument, format the macro in a more concise way."
(insert "Key: none\n")))
(when (and mac-counter mac-format)
(insert (format "Counter: %d\nFormat: \"%s\"\n" mac-counter mac-format))))
- (insert "\nMacro:\n\n")
+ (insert (format "\nMacro%s:\n\n" (if edmacro-reverse-macro-lines
+ " (most recent line first)"
+ "")))
(save-excursion
(insert fmtv "\n"))
(recenter '(4))
@@ -255,6 +279,33 @@ or nil, use a compact 80-column format."
;;; Commands for *Edit Macro* buffer.
+(defvar edmacro--skip-line-regexp
+ "[ \t]*\\($\\|;;\\|REM[ \t\n]\\)"
+ "A regexp identifying lines that should be ignored.")
+
+(defvar edmacro--command-line-regexp
+ "Command:[ \t]*\\([^ \t\n]*\\)[ \t]*$"
+ "A regexp identifying the line containing the command name.")
+
+(defvar edmacro--key-line-regexp
+ "Key:\\(.*\\)$"
+ "A regexp identifying the line containing the bound key sequence.")
+
+(defvar edmacro--counter-line-regexp
+ "Counter:[ \t]*\\([^ \t\n]*\\)[ \t]*$"
+ "A regexp identifying the line containing the counter value.")
+
+(defvar edmacro--format-line-regexp
+ "Format:[ \t]*\"\\([^\n]*\\)\"[ \t]*$"
+ "A regexp identifying the line containing the counter format.")
+
+(defvar edmacro--macro-lines-regexp
+ (rx "Macro"
+ (zero-or-one " (most recent line first)")
+ ":"
+ (zero-or-more (any " \t\n")))
+ "A regexp identifying the lines that precede the macro's contents.")
+
(defun edmacro-finish-edit ()
(interactive nil edmacro-mode)
(unless (eq major-mode 'edmacro-mode)
@@ -266,9 +317,9 @@ or nil, use a compact 80-column format."
(top (point-min)))
(goto-char top)
(let ((case-fold-search nil))
- (while (cond ((looking-at "[ \t]*\\($\\|;;\\|REM[ \t\n]\\)")
+ (while (cond ((looking-at edmacro--skip-line-regexp)
t)
- ((looking-at "Command:[ \t]*\\([^ \t\n]*\\)[ \t]*$")
+ ((looking-at edmacro--command-line-regexp)
(when edmacro-store-hook
(error "\"Command\" line not allowed in this context"))
(let ((str (match-string 1)))
@@ -283,7 +334,7 @@ or nil, use a compact 80-column format."
cmd)))
(keyboard-quit))))
t)
- ((looking-at "Key:\\(.*\\)$")
+ ((looking-at edmacro--key-line-regexp)
(when edmacro-store-hook
(error "\"Key\" line not allowed in this context"))
(let ((key (kbd (match-string 1))))
@@ -303,21 +354,21 @@ or nil, use a compact 80-column format."
(edmacro-format-keys key 1))))
(keyboard-quit))))))
t)
- ((looking-at "Counter:[ \t]*\\([^ \t\n]*\\)[ \t]*$")
+ ((looking-at edmacro--counter-line-regexp)
(when edmacro-store-hook
(error "\"Counter\" line not allowed in this context"))
(let ((str (match-string 1)))
(unless (equal str "")
(setq mac-counter (string-to-number str))))
t)
- ((looking-at "Format:[ \t]*\"\\([^\n]*\\)\"[ \t]*$")
+ ((looking-at edmacro--format-line-regexp)
(when edmacro-store-hook
(error "\"Format\" line not allowed in this context"))
(let ((str (match-string 1)))
(unless (equal str "")
(setq mac-format str)))
t)
- ((looking-at "Macro:[ \t\n]*")
+ ((looking-at edmacro--macro-lines-regexp)
(goto-char (match-end 0))
nil)
((eobp) nil)
@@ -336,7 +387,13 @@ or nil, use a compact 80-column format."
(when (buffer-name obuf)
(set-buffer obuf))
(message "Compiling keyboard macro...")
- (let ((mac (edmacro-parse-keys str)))
+ (let ((mac (edmacro-parse-keys (if edmacro-reverse-macro-lines
+ (with-temp-buffer
+ (insert str)
+ (reverse-region (point-min)
+ (point-max))
+ (buffer-string))
+ str))))
(message "Compiling keyboard macro...done")
(if store-hook
(funcall store-hook mac)
@@ -372,6 +429,36 @@ or nil, use a compact 80-column format."
(insert (edmacro-format-keys key t) "\n")
(insert (edmacro-format-keys key) " ")))
+(defun edmacro-set-macro-to-region-lines (beg end)
+ "Set the macro text to lines of text in the buffer between BEG and END.
+
+Interactively, BEG and END are the beginning and end of the
+region. If the region does not begin at the start of a line or
+if it does not end at the end of a line, the region is extended
+to include complete lines. If the region ends at the beginning
+of a line, that final line is excluded."
+ (interactive "*r" edmacro-mode)
+ ;; Use `save-excursion' to restore region if there are any errors.
+ ;; If there are no errors, update the macro text, then go to the
+ ;; beginning of the macro text.
+ (let ((final-position))
+ (save-excursion
+ (goto-char beg)
+ (unless (bolp) (setq beg (pos-bol)))
+ (goto-char end)
+ (unless (or (bolp) (eolp)) (setq end (pos-eol)))
+ (let ((text (buffer-substring beg end)))
+ (goto-char (point-min))
+ (if (not (let ((case-fold-search nil))
+ (re-search-forward edmacro--macro-lines-regexp nil t)))
+ (user-error "\"Macro:\" line not found")
+ (delete-region (match-end 0)
+ (point-max))
+ (goto-char (point-max))
+ (insert text)
+ (setq final-position (match-beginning 0)))))
+ (goto-char final-position)))
+
(defun edmacro-mode ()
"\\<edmacro-mode-map>Keyboard Macro Editing mode. Press \
\\[edmacro-finish-edit] to save and exit.
@@ -393,6 +480,10 @@ or \"none\" for no key bindings.
You can edit these lines to change the places where the new macro
is stored.
+Press \\[edmacro-set-macro-to-region-lines] to replace the text following the \"Macro:\" line
+with the text of the lines overlapping the region of text between
+point and mark. If that region ends at the beginning of a line,
+that final line is excluded.
Format of keyboard macros during editing:
@@ -629,17 +720,15 @@ This function assumes that the events can be stored in a string."
(setf (aref seq i) (logand (aref seq i) 127)))
seq)
-;; These are needed in a --without-x build.
-(defvar mouse-wheel-down-event)
-(defvar mouse-wheel-up-event)
-(defvar mouse-wheel-right-event)
-(defvar mouse-wheel-left-event)
-
(defun edmacro-fix-menu-commands (macro &optional noerror)
(if (vectorp macro)
(let (result)
;; Not preloaded in a --without-x build.
(require 'mwheel)
+ (defvar mouse-wheel-down-event)
+ (defvar mouse-wheel-up-event)
+ (defvar mouse-wheel-right-event)
+ (defvar mouse-wheel-left-event)
;; Make a list of the elements.
(setq macro (append macro nil))
(dolist (ev macro)
@@ -655,9 +744,9 @@ This function assumes that the events can be stored in a string."
;; info is recorded in macros to make this possible.
((or (mouse-event-p ev) (mouse-movement-p ev)
(memq (event-basic-type ev)
- (list mouse-wheel-down-event mouse-wheel-up-event
- mouse-wheel-right-event
- mouse-wheel-left-event)))
+ `( ,mouse-wheel-down-event ,mouse-wheel-up-event
+ ,mouse-wheel-right-event ,mouse-wheel-left-event
+ wheel-down wheel-up wheel-left wheel-right)))
nil)
(noerror nil)
(t