unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
* bug#65605: [PATCH] Command and option to make Edmacro better for long sequences
@ 2023-08-30  1:17 Okamsn via Bug reports for GNU Emacs, the Swiss army knife of text editors
       [not found] ` <handler.65605.B.169335831223584.ack@debbugs.gnu.org>
  2023-08-30 11:37 ` Eli Zaretskii
  0 siblings, 2 replies; 8+ messages in thread
From: Okamsn via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2023-08-30  1:17 UTC (permalink / raw)
  To: 65605

[-- Attachment #1: Type: text/plain, Size: 882 bytes --]

Hello,

The attached patch adds the command `edmacro-set-macro-to-region-lines` 
for quickly reducing the presented keys to those in the region, and adds 
the user option `edmacro-reverse-key-order` for showing the most recent 
keys first.

These two changes are useful for `kmacro-edit-lossage`.  My use case is 
that I would like to quickly create a keyboard macro from some of my 
recent key presses, but Edmacro is slow to use when the lossage size is 
large.  When it is large, I must jump to the bottom of the buffer, then 
cut the desired lines, then jump back up, then delete the remaining 
lines, then paste the cut lines, then press `C-c C-c`. If I use 
`delete-selection-mode`, I can combine two of the steps.  With this new 
change from the patch, I can move down a few lines, select a few lines, 
then press `C-c C-r`, then press `C-c C-c`.

Thank you.

[-- Attachment #2: 0001-Make-using-Edmacro-better-for-long-sequences-of-keys.patch --]
[-- Type: text/x-patch, Size: 8611 bytes --]

From 380bc64ab9a5a4acadcdee3dccde49a7d1002e69 Mon Sep 17 00:00:00 2001
From: Earl Hyatt <okamsn@protonmail.com>
Date: Sat, 19 Aug 2023 18:26:45 -0400
Subject: [PATCH] Make using Edmacro better for long sequences of keys.

* lisp/edmacro.el (edmacro-set-macro-to-region-lines,
edmacro-reverse-key-order): New command and user option to
make working with longer lists of keys (such as 'kmacro-edit-lossage')
easier.
(edit-kbd-macro): Move regexps used to identify pars of buffer
to internal variables.
(edmacro--macro-lines-regexp): Allow noting whether the most
recent keys are displayed first.
(edmacro-mode-font-lock-keywords): Allow noting whether the most
recent keys are displayed first.
---
 lisp/edmacro.el | 108 +++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 97 insertions(+), 11 deletions(-)

diff --git a/lisp/edmacro.el b/lisp/edmacro.el
index 69d20d2bad3..a2662f9a635 100644
--- a/lisp/edmacro.el
+++ b/lisp/edmacro.el
@@ -73,9 +73,18 @@ edmacro-eight-bits
   :type 'boolean
   :group 'kmacro)
 
+(defcustom edmacro-reverse-key-order nil
+  "Non-nil if `edit-kbd-macro' should show the most recent keys first.
+
+This is useful when dealing with long lists of key sequences, such as
+from `kmacro-edit-lossage'."
+  :type 'boolean
+  :group 'kmacro)
+
 (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,7 +97,10 @@ edmacro-label
   :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 first)")))
+                     ":"))
+     0 'edmacro-label)
     (,(rx bol
           (group ";; Keyboard Macro Editor.  Press ")
           (group (*? nonl))
@@ -166,7 +178,13 @@ edit-kbd-macro
       (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-key-order)
+                         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 +199,9 @@ edit-kbd-macro
         (setq-local font-lock-defaults
                     '(edmacro-mode-font-lock-keywords nil nil nil nil))
         (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-key-order)
 	(erase-buffer)
         (insert (substitute-command-keys
                  (concat
@@ -202,7 +223,11 @@ edit-kbd-macro
 	      (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 "\nMacro"
+                (if edmacro-reverse-key-order
+                    " (most recent first)"
+                  "")
+                ":\n\n")
 	(save-excursion
 	  (insert fmtv "\n"))
 	(recenter '(4))
@@ -255,6 +280,33 @@ format-kbd-macro
 \f
 ;;; 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 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 +318,9 @@ edmacro-finish-edit
 	(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 +335,7 @@ edmacro-finish-edit
                                     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 +355,21 @@ edmacro-finish-edit
                                         (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 +388,13 @@ edmacro-finish-edit
 	(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-key-order
+                                           (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 +430,34 @@ edmacro-insert-key
       (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 the lines of the region.
+
+If BEG is not at the beginning of a line, it is moved to the
+beginning of the line.  If END is at the beginning of a line,
+that line is excluded.  Otherwise, if END is not at the
+end of a line, it is moved to the end of the line."
+  (interactive "r" edmacro-mode)
+  ;; Use `save-excursion' to restore region if there are any errors.
+  ;; If there are no errors, update macro contents, then go to the
+  ;; beginning of the macro contents.
+  (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))
+          (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.
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* bug#65605: [PATCH] Command and option to make Edmacro better for long sequences
       [not found] ` <handler.65605.B.169335831223584.ack@debbugs.gnu.org>
@ 2023-08-30  1:21   ` Okamsn via Bug reports for GNU Emacs, the Swiss army knife of text editors
  0 siblings, 0 replies; 8+ messages in thread
From: Okamsn via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2023-08-30  1:21 UTC (permalink / raw)
  To: 65605

[-- Attachment #1: Type: text/plain, Size: 110 bytes --]

There was a typo in the commit message of the previous attached file. 
This new attached file fixes the typo.

[-- Attachment #2: v2-0001-Make-using-Edmacro-better-for-long-sequences-of-k.patch --]
[-- Type: text/x-patch, Size: 8550 bytes --]

From b33d766702c51881cbafcc649dc63c3019f34007 Mon Sep 17 00:00:00 2001
From: Earl Hyatt <okamsn@protonmail.com>
Date: Sat, 19 Aug 2023 18:26:45 -0400
Subject: [PATCH v2] Make using Edmacro better for long sequences of keys.

* lisp/edmacro.el (edmacro-set-macro-to-region-lines,
edmacro-reverse-key-order): New command and user option to
make working with longer lists of keys (such as 'kmacro-edit-lossage')
easier.
(edit-kbd-macro): Move regexps used to identify parts of buffer
to internal variables.
(edmacro--macro-lines-regexp, edmacro-mode-font-lock-keywords): Allow
noting whether the most recent keys are displayed first.
---
 lisp/edmacro.el | 108 +++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 97 insertions(+), 11 deletions(-)

diff --git a/lisp/edmacro.el b/lisp/edmacro.el
index 69d20d2bad3..a2662f9a635 100644
--- a/lisp/edmacro.el
+++ b/lisp/edmacro.el
@@ -73,9 +73,18 @@ edmacro-eight-bits
   :type 'boolean
   :group 'kmacro)
 
+(defcustom edmacro-reverse-key-order nil
+  "Non-nil if `edit-kbd-macro' should show the most recent keys first.
+
+This is useful when dealing with long lists of key sequences, such as
+from `kmacro-edit-lossage'."
+  :type 'boolean
+  :group 'kmacro)
+
 (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,7 +97,10 @@ edmacro-label
   :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 first)")))
+                     ":"))
+     0 'edmacro-label)
     (,(rx bol
           (group ";; Keyboard Macro Editor.  Press ")
           (group (*? nonl))
@@ -166,7 +178,13 @@ edit-kbd-macro
       (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-key-order)
+                         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 +199,9 @@ edit-kbd-macro
         (setq-local font-lock-defaults
                     '(edmacro-mode-font-lock-keywords nil nil nil nil))
         (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-key-order)
 	(erase-buffer)
         (insert (substitute-command-keys
                  (concat
@@ -202,7 +223,11 @@ edit-kbd-macro
 	      (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 "\nMacro"
+                (if edmacro-reverse-key-order
+                    " (most recent first)"
+                  "")
+                ":\n\n")
 	(save-excursion
 	  (insert fmtv "\n"))
 	(recenter '(4))
@@ -255,6 +280,33 @@ format-kbd-macro
 \f
 ;;; 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 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 +318,9 @@ edmacro-finish-edit
 	(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 +335,7 @@ edmacro-finish-edit
                                     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 +355,21 @@ edmacro-finish-edit
                                         (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 +388,13 @@ edmacro-finish-edit
 	(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-key-order
+                                           (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 +430,34 @@ edmacro-insert-key
       (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 the lines of the region.
+
+If BEG is not at the beginning of a line, it is moved to the
+beginning of the line.  If END is at the beginning of a line,
+that line is excluded.  Otherwise, if END is not at the
+end of a line, it is moved to the end of the line."
+  (interactive "r" edmacro-mode)
+  ;; Use `save-excursion' to restore region if there are any errors.
+  ;; If there are no errors, update macro contents, then go to the
+  ;; beginning of the macro contents.
+  (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))
+          (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.
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* bug#65605: [PATCH] Command and option to make Edmacro better for long sequences
  2023-08-30  1:17 bug#65605: [PATCH] Command and option to make Edmacro better for long sequences Okamsn via Bug reports for GNU Emacs, the Swiss army knife of text editors
       [not found] ` <handler.65605.B.169335831223584.ack@debbugs.gnu.org>
@ 2023-08-30 11:37 ` Eli Zaretskii
  2023-09-03 16:05   ` Okamsn via Bug reports for GNU Emacs, the Swiss army knife of text editors
  1 sibling, 1 reply; 8+ messages in thread
From: Eli Zaretskii @ 2023-08-30 11:37 UTC (permalink / raw)
  To: Okamsn; +Cc: 65605

> Date: Wed, 30 Aug 2023 01:17:51 +0000
> From:  Okamsn via "Bug reports for GNU Emacs,
>  the Swiss army knife of text editors" <bug-gnu-emacs@gnu.org>
> 
> The attached patch adds the command `edmacro-set-macro-to-region-lines` 
> for quickly reducing the presented keys to those in the region, and adds 
> the user option `edmacro-reverse-key-order` for showing the most recent 
> keys first.

Thanks.

> These two changes are useful for `kmacro-edit-lossage`.  My use case is 
> that I would like to quickly create a keyboard macro from some of my 
> recent key presses, but Edmacro is slow to use when the lossage size is 
> large.  When it is large, I must jump to the bottom of the buffer, then 
> cut the desired lines, then jump back up, then delete the remaining 
> lines, then paste the cut lines, then press `C-c C-c`. If I use 
> `delete-selection-mode`, I can combine two of the steps.  With this new 
> change from the patch, I can move down a few lines, select a few lines, 
> then press `C-c C-r`, then press `C-c C-c`.

If these commands are indeed important conveniences, they should be in
the manual, I think.

> +(defcustom edmacro-reverse-key-order nil
> +  "Non-nil if `edit-kbd-macro' should show the most recent keys first.
> +
> +This is useful when dealing with long lists of key sequences, such as
> +from `kmacro-edit-lossage'."
> +  :type 'boolean
> +  :group 'kmacro)

Defcustoms should have the :version tag.

> +(defun edmacro-set-macro-to-region-lines (beg end)
> +  "Set the Macro text to the lines of the region.

We prefer to have the mandatory arguments mentioned in the first line
of a function's doc string.

> +If BEG is not at the beginning of a line, it is moved to the
> +beginning of the line.  If END is at the beginning of a line,
> +that line is excluded.  Otherwise, if END is not at the
> +end of a line, it is moved to the end of the line."

This describes the implementation, whereas this is a command, so the
doc string should have users, not programmer's in mind.  Try to
describe BEG and END in user-level terms, for example:

  Macro text will start and the beginning of line containing buffer
  position BEG.

Also, the doc string should tell how BEG and END are determined in
interactive invocations.






^ permalink raw reply	[flat|nested] 8+ messages in thread

* bug#65605: [PATCH] Command and option to make Edmacro better for long sequences
  2023-08-30 11:37 ` Eli Zaretskii
@ 2023-09-03 16:05   ` Okamsn via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2023-09-03 18:36     ` Stefan Kangas
  2023-09-04 11:05     ` Eli Zaretskii
  0 siblings, 2 replies; 8+ messages in thread
From: Okamsn via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2023-09-03 16:05 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 65605

[-- Attachment #1: Type: text/plain, Size: 1197 bytes --]

Eli Zaretskii wrote:
> If these commands are indeed important conveniences, they should be in
> the manual, I think.

I have described the new command and `edmacro-insert-key` in the manual.

> Defcustoms should have the :version tag.

I have given it as "30.1".

> 
>> +(defun edmacro-set-macro-to-region-lines (beg end)
>> +  "Set the Macro text to the lines of the region.
> 
> We prefer to have the mandatory arguments mentioned in the first line
> of a function's doc string.
> 
>> +If BEG is not at the beginning of a line, it is moved to the
>> +beginning of the line.  If END is at the beginning of a line,
>> +that line is excluded.  Otherwise, if END is not at the
>> +end of a line, it is moved to the end of the line."
> 
> This describes the implementation, whereas this is a command, so the
> doc string should have users, not programmer's in mind.  Try to
> describe BEG and END in user-level terms, for example:
> 
>    Macro text will start and the beginning of line containing buffer
>    position BEG.
> 
> Also, the doc string should tell how BEG and END are determined in
> interactive invocations.
> 

I have changed it. How does it look now?

[-- Attachment #2: v3-0001-Make-using-Edmacro-better-for-long-sequences-of-k.patch --]
[-- Type: text/x-patch, Size: 10361 bytes --]

From f1365193df83f7a765abc95507bbddb0d5d5a929 Mon Sep 17 00:00:00 2001
From: Earl Hyatt <okamsn@protonmail.com>
Date: Sat, 19 Aug 2023 18:26:45 -0400
Subject: [PATCH v3] Make using Edmacro better for long sequences of keys.

* lisp/edmacro.el (edmacro-set-macro-to-region-lines,
edmacro-reverse-key-order): New command and user option to
make working with longer lists of keys (such as from
'kmacro-edit-lossage') easier.
(edit-kbd-macro): Move regexps used to identify parts of buffer
to internal variables.
(edmacro--macro-lines-regexp, edmacro-mode-font-lock-keywords): Allow
noting whether the most recent line of keys is displayed first.
(edmacro-mode-map): Bind the new command to 'C-c C-r'.
(edmacro-mode): Describe the new command in the mode documentation
string.

* doc/emacs/kmacro.texi (Edit Keyboard Macro): Mention
'edmacro-insert-key' and the newly added
'edmacro-set-macro-to-region-lines'.
---
 doc/emacs/kmacro.texi |  11 ++++
 lisp/edmacro.el       | 113 ++++++++++++++++++++++++++++++++++++++----
 2 files changed, 113 insertions(+), 11 deletions(-)

diff --git a/doc/emacs/kmacro.texi b/doc/emacs/kmacro.texi
index fc1402b489d..30201cc71c3 100644
--- a/doc/emacs/kmacro.texi
+++ b/doc/emacs/kmacro.texi
@@ -515,6 +515,17 @@ Edit Keyboard Macro
 of how to edit the macro.  When you are finished editing, type
 @kbd{C-c C-c}.
 
+@findex edmacro-insert-key
+@findex edmacro-set-macro-to-region-lines
+  The mode provides commands for more easily editing the formatted
+macro.  Use @kbd{C-c C-q} (@code{edmacro-insert-key}) to insert the
+next key sequence that you type into the buffer using the correct
+format, similar to @kbd{C-q} (@code{quoted-insert}).  Use @kbd{C-c
+C-r} (@code{edmacro-set-macro-to-region-lines}) to replace the macro's
+formatted text with the lines overlapping the region of text between
+point and mark.  If the region ends at the beginning of a line, that
+final line is excluded.
+
 @findex edit-kbd-macro
 @kindex C-x C-k e
   You can edit a named keyboard macro or a macro bound to a key by typing
diff --git a/lisp/edmacro.el b/lisp/edmacro.el
index bbf5a7f0495..9bc37e47622 100644
--- a/lisp/edmacro.el
+++ b/lisp/edmacro.el
@@ -73,9 +73,19 @@ edmacro-eight-bits
   :type 'boolean
   :group 'kmacro)
 
+(defcustom edmacro-reverse-key-order nil
+  "Non-nil if `edit-kbd-macro' should show the most recent line of keys 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,7 +98,10 @@ edmacro-label
   :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 (*? nonl))
@@ -166,7 +179,13 @@ edit-kbd-macro
       (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-key-order)
+                         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 @@ edit-kbd-macro
         (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-key-order)
 	(erase-buffer)
         (insert (substitute-command-keys
                  (concat
@@ -202,7 +224,9 @@ edit-kbd-macro
 	      (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-key-order
+                                             " (most recent line first)"
+                                           "")))
 	(save-excursion
 	  (insert fmtv "\n"))
 	(recenter '(4))
@@ -255,6 +279,33 @@ format-kbd-macro
 \f
 ;;; 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 @@ edmacro-finish-edit
 	(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 @@ edmacro-finish-edit
                                     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 @@ edmacro-finish-edit
                                         (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 @@ edmacro-finish-edit
 	(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-key-order
+                                           (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 @@ edmacro-insert-key
       (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 the lines overlapping the buffer text from BEG to END.
+
+When called interactively, this command uses the beginning and
+end of the selected region as the buffer positions.
+
+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 contents, then go to the
+  ;; beginning of the macro contents.
+  (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 @@ edmacro-mode
 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:
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* bug#65605: [PATCH] Command and option to make Edmacro better for long sequences
  2023-09-03 16:05   ` Okamsn via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2023-09-03 18:36     ` Stefan Kangas
  2023-09-04 11:05     ` Eli Zaretskii
  1 sibling, 0 replies; 8+ messages in thread
From: Stefan Kangas @ 2023-09-03 18:36 UTC (permalink / raw)
  To: Okamsn, Eli Zaretskii; +Cc: 65605

Okamsn via "Bug reports for GNU Emacs, the Swiss army knife of text
editors" <bug-gnu-emacs@gnu.org> writes:

> From f1365193df83f7a765abc95507bbddb0d5d5a929 Mon Sep 17 00:00:00 2001
> From: Earl Hyatt <okamsn@protonmail.com>
> Date: Sat, 19 Aug 2023 18:26:45 -0400
> Subject: [PATCH v3] Make using Edmacro better for long sequences of keys.
>
> * lisp/edmacro.el (edmacro-set-macro-to-region-lines,
> edmacro-reverse-key-order): New command and user option to
> make working with longer lists of keys (such as from
> 'kmacro-edit-lossage') easier.
> (edit-kbd-macro): Move regexps used to identify parts of buffer
> to internal variables.
> (edmacro--macro-lines-regexp, edmacro-mode-font-lock-keywords): Allow
> noting whether the most recent line of keys is displayed first.
> (edmacro-mode-map): Bind the new command to 'C-c C-r'.
> (edmacro-mode): Describe the new command in the mode documentation
> string.

Thanks, this looks like a useful feature.

Your patch LGTM, but I think it deserves to be called out in NEWS.





^ permalink raw reply	[flat|nested] 8+ messages in thread

* bug#65605: [PATCH] Command and option to make Edmacro better for long sequences
  2023-09-03 16:05   ` Okamsn via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2023-09-03 18:36     ` Stefan Kangas
@ 2023-09-04 11:05     ` Eli Zaretskii
  2023-09-09  0:45       ` Okamsn via Bug reports for GNU Emacs, the Swiss army knife of text editors
  1 sibling, 1 reply; 8+ messages in thread
From: Eli Zaretskii @ 2023-09-04 11:05 UTC (permalink / raw)
  To: Okamsn; +Cc: 65605

> Date: Sun, 03 Sep 2023 16:05:03 +0000
> From: Okamsn <okamsn@protonmail.com>
> Cc: 65605@debbugs.gnu.org
> 
> >> +If BEG is not at the beginning of a line, it is moved to the
> >> +beginning of the line.  If END is at the beginning of a line,
> >> +that line is excluded.  Otherwise, if END is not at the
> >> +end of a line, it is moved to the end of the line."
> > 
> > This describes the implementation, whereas this is a command, so the
> > doc string should have users, not programmer's in mind.  Try to
> > describe BEG and END in user-level terms, for example:
> > 
> >    Macro text will start and the beginning of line containing buffer
> >    position BEG.
> > 
> > Also, the doc string should tell how BEG and END are determined in
> > interactive invocations.
> > 
> 
> I have changed it. How does it look now?

See some comments below.

> +@findex edmacro-insert-key
> +@findex edmacro-set-macro-to-region-lines
> +  The mode provides commands for more easily editing the formatted

You say "The mode", but it is not clear what is "the mode" to which
you allude.

> +macro.  Use @kbd{C-c C-q} (@code{edmacro-insert-key}) to insert the
> +next key sequence that you type into the buffer using the correct
> +format, similar to @kbd{C-q} (@code{quoted-insert}).  Use @kbd{C-c
> +C-r} (@code{edmacro-set-macro-to-region-lines}) to replace the macro's
> +formatted text with the lines overlapping the region of text between
> +point and mark.

Here, "the lines overlapping the region of text between point and
mark" is IMO not clear enough.  I think you mean to say "text in the
region", and the "overlapping" part is just to allude to the fact that
the region might begin and/or end in the middle of a line?  If so, I
would suggest

  Use @kbd{C-c C-r} (@code{edmacro-set-macro-to-region-lines}) to
  replace the macro's formatted text with the text in the region.  If
  the region begins not and beginning of a line or ends not at end of
  line, it is extended to include complete lines.

Also, is the "formatted" in "macro's formatted text" really needed?
What does "formatted" signify?

> +(defcustom edmacro-reverse-key-order nil
> +  "Non-nil if `edit-kbd-macro' should show the most recent line of keys first.
                                                                      ^^^^
"Keys" or "key sequence"?

> +(defun edmacro-set-macro-to-region-lines (beg end)
> +  "Set the macro text to the lines overlapping the buffer text from BEG to END.

I would suggest

  Set macro text to lines of text in current buffer between BEG and END.

> +When called interactively, this command uses the beginning and
> +end of the selected region as the buffer positions.

  Interactively, BEG and END are the beginning and end of the region.






^ permalink raw reply	[flat|nested] 8+ messages in thread

* bug#65605: [PATCH] Command and option to make Edmacro better for long sequences
  2023-09-04 11:05     ` Eli Zaretskii
@ 2023-09-09  0:45       ` Okamsn via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2023-09-16  9:55         ` Eli Zaretskii
  0 siblings, 1 reply; 8+ messages in thread
From: Okamsn via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2023-09-09  0:45 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 65605, stefankangas

[-- Attachment #1: Type: text/plain, Size: 3753 bytes --]

Eli Zaretskii wrote:
>> Date: Sun, 03 Sep 2023 16:05:03 +0000
>> From: Okamsn <okamsn@protonmail.com>
>> Cc: 65605@debbugs.gnu.org
>>
>>>> +If BEG is not at the beginning of a line, it is moved to the
>>>> +beginning of the line.  If END is at the beginning of a line,
>>>> +that line is excluded.  Otherwise, if END is not at the
>>>> +end of a line, it is moved to the end of the line."
>>>
>>> This describes the implementation, whereas this is a command, so the
>>> doc string should have users, not programmer's in mind.  Try to
>>> describe BEG and END in user-level terms, for example:
>>>
>>>     Macro text will start and the beginning of line containing buffer
>>>     position BEG.
>>>
>>> Also, the doc string should tell how BEG and END are determined in
>>> interactive invocations.
>>>
>>
>> I have changed it. How does it look now?
> 
> See some comments below.
> 
>> +@findex edmacro-insert-key
>> +@findex edmacro-set-macro-to-region-lines
>> +  The mode provides commands for more easily editing the formatted
> 
> You say "The mode", but it is not clear what is "the mode" to which
> you allude.
> 
>> +macro.  Use @kbd{C-c C-q} (@code{edmacro-insert-key}) to insert the
>> +next key sequence that you type into the buffer using the correct
>> +format, similar to @kbd{C-q} (@code{quoted-insert}).  Use @kbd{C-c
>> +C-r} (@code{edmacro-set-macro-to-region-lines}) to replace the macro's
>> +formatted text with the lines overlapping the region of text between
>> +point and mark.
> 
> Here, "the lines overlapping the region of text between point and
> mark" is IMO not clear enough.  I think you mean to say "text in the
> region", and the "overlapping" part is just to allude to the fact that
> the region might begin and/or end in the middle of a line?  If so, I
> would suggest
> 
>    Use @kbd{C-c C-r} (@code{edmacro-set-macro-to-region-lines}) to
>    replace the macro's formatted text with the text in the region.  If
>    the region begins not and beginning of a line or ends not at end of
>    line, it is extended to include complete lines.
> 
> Also, is the "formatted" in "macro's formatted text" really needed?
> What does "formatted" signify?

I wasn't sure what to call the lines of formatted text showing the 
macro's contents. I have changed it to just "text".

>> +(defcustom edmacro-reverse-key-order nil
>> +  "Non-nil if `edit-kbd-macro' should show the most recent line of keys first.
>                                                                        ^^^^
> "Keys" or "key sequence"?

I have changed it to "key sequences".

>> +(defun edmacro-set-macro-to-region-lines (beg end)
>> +  "Set the macro text to the lines overlapping the buffer text from BEG to END.
> 
> I would suggest
> 
>    Set macro text to lines of text in current buffer between BEG and END.
> 
>> +When called interactively, this command uses the beginning and
>> +end of the selected region as the buffer positions.
> 
>    Interactively, BEG and END are the beginning and end of the region.
> 

I have changed it to:

"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."

I have also added the new command and user option to "NEWS" as requested 
by Stefan Kangas, and have described the new user option in the manual.

I have also changed the name of the user option to 
"edmacro-reverse-macro-lines", which is a bit clearer.

[-- Attachment #2: v4-0001-Make-using-Edmacro-better-for-long-sequences-of-k.patch --]
[-- Type: text/x-patch, Size: 12278 bytes --]

From faa306de80cae416c1079a8dd902617c36664448 Mon Sep 17 00:00:00 2001
From: Earl Hyatt <okamsn@protonmail.com>
Date: Sat, 19 Aug 2023 18:26:45 -0400
Subject: [PATCH v4] Make using Edmacro better for long sequences of keys.

* lisp/edmacro.el (edmacro-set-macro-to-region-lines,
edmacro-reverse-key-order): New command and user option to
make working with longer lists of keys (such as from
'kmacro-edit-lossage') easier.
(edit-kbd-macro): Move regexps used to identify parts of buffer
to internal variables.
(edmacro--macro-lines-regexp, edmacro-mode-font-lock-keywords): Allow
noting whether the most recent line of keys is displayed first.
(edmacro-mode-map): Bind the new command to 'C-c C-r'.
(edmacro-mode): Describe the new command in the mode documentation
string.

* doc/emacs/kmacro.texi (Edit Keyboard Macro): Mention
'edmacro-insert-key' and the newly added
'edmacro-set-macro-to-region-lines' and
'edmacro-reverse-key-line-order'.

* etc/NEWS (Edmacro): Add section describing the new features.
---
 doc/emacs/kmacro.texi |  19 ++++++-
 etc/NEWS              |  14 ++++++
 lisp/edmacro.el       | 113 ++++++++++++++++++++++++++++++++++++++----
 3 files changed, 134 insertions(+), 12 deletions(-)

diff --git a/doc/emacs/kmacro.texi b/doc/emacs/kmacro.texi
index fc1402b489d..05095bc68b8 100644
--- a/doc/emacs/kmacro.texi
+++ b/doc/emacs/kmacro.texi
@@ -515,6 +515,19 @@ Edit Keyboard Macro
 of how to edit the macro.  When you are finished editing, type
 @kbd{C-c C-c}.
 
+@findex edmacro-insert-key
+@findex edmacro-set-macro-to-region-lines
+  @code{edmacro-mode}, the major mode used by
+@code{kmacro-edit-macro}, provides commands for more easily editing
+the formatted macro.  Use @kbd{C-c C-q} (@code{edmacro-insert-key}) to
+insert the next key sequence that you type into the buffer using the
+correct format, similar to @kbd{C-q} (@code{quoted-insert}).  Use
+@kbd{C-c C-r} (@code{edmacro-set-macro-to-region-lines}) to replace
+the macro's text with the text in 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.
+
 @findex edit-kbd-macro
 @kindex C-x C-k e
   You can edit a named keyboard macro or a macro bound to a key by typing
@@ -523,9 +536,13 @@ Edit Keyboard Macro
 @kbd{M-x @var{name}} or some other key sequence.
 
 @findex kmacro-edit-lossage
+@vindex edmacro-reverse-macro-lines
 @kindex C-x C-k l
   You can edit the last 300 keystrokes as a macro by typing
-@kbd{C-x C-k l} (@code{kmacro-edit-lossage}).
+@kbd{C-x C-k l} (@code{kmacro-edit-lossage}).  By default,
+your most recent keystrokes are listed at the bottom of the buffer.
+To list a macro's key sequences in reverse order, set
+@code{edmacro-reverse-macro-lines} to @code{t}.
 
 @node Keyboard Macro Step-Edit
 @section Stepwise Editing a Keyboard Macro
diff --git a/etc/NEWS b/etc/NEWS
index bbf4b67fe34..546d7b34de7 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -734,6 +734,20 @@ neither of which have been supported by Emacs since version 23.1.
 The user option 'url-gateway-nslookup-program' and the function
 'url-gateway-nslookup-host' are consequently also obsolete.
 
++++
+** Edmacro
+
+*** New command 'edmacro-set-macro-to-region-lines'.
+Bound to 'C-c C-r', this command replaces the macro text with the
+lines of the region.  If needed, the region is extended to include
+whole lines.  If the region ends at the beginning of a line, that last
+line is excluded.
+
+*** New user option 'edmacro-reverse-macro-lines'.
+When this is non-nil, the lines of key sequences are displayed with
+the most recent line fist.  This is can be useful when working with
+macros with many lines, such as from 'kmacro-edit-lossage'.
+
 \f
 * New Modes and Packages in Emacs 30.1
 
diff --git a/lisp/edmacro.el b/lisp/edmacro.el
index bbf5a7f0495..6cad8158321 100644
--- a/lisp/edmacro.el
+++ b/lisp/edmacro.el
@@ -73,9 +73,19 @@ edmacro-eight-bits
   :type 'boolean
   :group 'kmacro)
 
+(defcustom edmacro-reverse-macro-lines nil
+  "Non-nil if `edit-kbd-macro' should show the 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,7 +98,10 @@ edmacro-label
   :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 (*? nonl))
@@ -166,7 +179,13 @@ edit-kbd-macro
       (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 @@ edit-kbd-macro
         (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 @@ edit-kbd-macro
 	      (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 @@ format-kbd-macro
 \f
 ;;; 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 @@ edmacro-finish-edit
 	(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 @@ edmacro-finish-edit
                                     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 @@ edmacro-finish-edit
                                         (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 @@ edmacro-finish-edit
 	(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 @@ edmacro-insert-key
       (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 @@ edmacro-mode
 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:
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* bug#65605: [PATCH] Command and option to make Edmacro better for long sequences
  2023-09-09  0:45       ` Okamsn via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2023-09-16  9:55         ` Eli Zaretskii
  0 siblings, 0 replies; 8+ messages in thread
From: Eli Zaretskii @ 2023-09-16  9:55 UTC (permalink / raw)
  To: Okamsn; +Cc: 65605-done, stefankangas

> Date: Sat, 09 Sep 2023 00:45:05 +0000
> From: Okamsn <okamsn@protonmail.com>
> Cc: 65605@debbugs.gnu.org, stefankangas@gmail.com
> 
> Eli Zaretskii wrote:
> >> Date: Sun, 03 Sep 2023 16:05:03 +0000
> >> From: Okamsn <okamsn@protonmail.com>
> >> Cc: 65605@debbugs.gnu.org
> >>
> >>>> +If BEG is not at the beginning of a line, it is moved to the
> >>>> +beginning of the line.  If END is at the beginning of a line,
> >>>> +that line is excluded.  Otherwise, if END is not at the
> >>>> +end of a line, it is moved to the end of the line."
> >>>
> >>> This describes the implementation, whereas this is a command, so the
> >>> doc string should have users, not programmer's in mind.  Try to
> >>> describe BEG and END in user-level terms, for example:
> >>>
> >>>     Macro text will start and the beginning of line containing buffer
> >>>     position BEG.
> >>>
> >>> Also, the doc string should tell how BEG and END are determined in
> >>> interactive invocations.
> >>>
> >>
> >> I have changed it. How does it look now?
> > 
> > See some comments below.
> > 
> >> +@findex edmacro-insert-key
> >> +@findex edmacro-set-macro-to-region-lines
> >> +  The mode provides commands for more easily editing the formatted
> > 
> > You say "The mode", but it is not clear what is "the mode" to which
> > you allude.
> > 
> >> +macro.  Use @kbd{C-c C-q} (@code{edmacro-insert-key}) to insert the
> >> +next key sequence that you type into the buffer using the correct
> >> +format, similar to @kbd{C-q} (@code{quoted-insert}).  Use @kbd{C-c
> >> +C-r} (@code{edmacro-set-macro-to-region-lines}) to replace the macro's
> >> +formatted text with the lines overlapping the region of text between
> >> +point and mark.
> > 
> > Here, "the lines overlapping the region of text between point and
> > mark" is IMO not clear enough.  I think you mean to say "text in the
> > region", and the "overlapping" part is just to allude to the fact that
> > the region might begin and/or end in the middle of a line?  If so, I
> > would suggest
> > 
> >    Use @kbd{C-c C-r} (@code{edmacro-set-macro-to-region-lines}) to
> >    replace the macro's formatted text with the text in the region.  If
> >    the region begins not and beginning of a line or ends not at end of
> >    line, it is extended to include complete lines.
> > 
> > Also, is the "formatted" in "macro's formatted text" really needed?
> > What does "formatted" signify?
> 
> I wasn't sure what to call the lines of formatted text showing the 
> macro's contents. I have changed it to just "text".
> 
> >> +(defcustom edmacro-reverse-key-order nil
> >> +  "Non-nil if `edit-kbd-macro' should show the most recent line of keys first.
> >                                                                        ^^^^
> > "Keys" or "key sequence"?
> 
> I have changed it to "key sequences".
> 
> >> +(defun edmacro-set-macro-to-region-lines (beg end)
> >> +  "Set the macro text to the lines overlapping the buffer text from BEG to END.
> > 
> > I would suggest
> > 
> >    Set macro text to lines of text in current buffer between BEG and END.
> > 
> >> +When called interactively, this command uses the beginning and
> >> +end of the selected region as the buffer positions.
> > 
> >    Interactively, BEG and END are the beginning and end of the region.
> > 
> 
> I have changed it to:
> 
> "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."
> 
> I have also added the new command and user option to "NEWS" as requested 
> by Stefan Kangas, and have described the new user option in the manual.
> 
> I have also changed the name of the user option to 
> "edmacro-reverse-macro-lines", which is a bit clearer.

Thanks, installed on the master branch, and closing the bug.





^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2023-09-16  9:55 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-08-30  1:17 bug#65605: [PATCH] Command and option to make Edmacro better for long sequences Okamsn via Bug reports for GNU Emacs, the Swiss army knife of text editors
     [not found] ` <handler.65605.B.169335831223584.ack@debbugs.gnu.org>
2023-08-30  1:21   ` Okamsn via Bug reports for GNU Emacs, the Swiss army knife of text editors
2023-08-30 11:37 ` Eli Zaretskii
2023-09-03 16:05   ` Okamsn via Bug reports for GNU Emacs, the Swiss army knife of text editors
2023-09-03 18:36     ` Stefan Kangas
2023-09-04 11:05     ` Eli Zaretskii
2023-09-09  0:45       ` Okamsn via Bug reports for GNU Emacs, the Swiss army knife of text editors
2023-09-16  9:55         ` Eli Zaretskii

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).