unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
* bug#61549: 30.0.50; [PATCH] New keyboard macro counter functions
@ 2023-02-16  8:17 Alex Bochannek
  2023-02-17  8:13 ` Eli Zaretskii
  0 siblings, 1 reply; 22+ messages in thread
From: Alex Bochannek @ 2023-02-16  8:17 UTC (permalink / raw)
  To: 61549

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

Hello!

I have been working on blog posts about keyboard macros and found that
it would be useful to have comparison functions for the keyboard macro
counter.

I implemented two functions to load and save macro counter values from
and to number registers; three comparison functions of the macro counter
with a number register that conditionally increment the counter; three
comparison functions of the macro counter with a prefix that terminate
the macro execution. This simplifies handling multiple counters and
conditional macro termination.

I am attaching the changes to:
  emacs.texi
  kmacro.texi
  NEWS
  kmacro.el
  kmacro-tests.el

I hope this functionality is useful and that I followed the coding and
style standards.

Thanks!

	Advanced keyboard macro counter commands for register
	integration and conditional macro termination

	* doc/emacs/emacs.texi (Top):
	Document advanced keyboard macro counter commands.

	* doc/emacs/kmacro.texi (Keyboard Macros, Keyboard Macro Counter):
	Document advanced keyboard macro counter commands.

	* etc/NEWS:
	Document advanced keyboard macro counter commands.

	* lisp/kmacro.el (kmacro-keymap, kmacro-reg-load-counter)
	(kmacro-reg-save-counter, kmacro-reg-add-counter-equal)
	(kmacro-reg-add-counter-equal, kmacro-reg-add-counter-less)
	(kmacro-reg-add-counter-greater, kmacro-reg-add-counter)
	(kmacro-quit-counter-equal, kmacro-quit-counter-less)
	(kmacro-quit-counter-greater, kmacro-quit-counter):
	Add advanced keyboard macro counter commands to kmacro keymap.
	Implement advanced keyboard macro counter commands.

	* test/lisp/kmacro-tests.el (kmacro-tests-test-reg-load)
	(kmacro-tests-test-reg-save)
	(kmacro-tests-test-reg-add-counter-equal-01)
	(kmacro-tests-test-reg-add-counter-equal-02)
	(kmacro-tests-test-reg-add-counter-equal-03)
	(kmacro-tests-test-reg-add-counter-equal-04)
	(kmacro-tests-test-reg-add-counter-less)
	(kmacro-tests-test-reg-add-counter-greater)
	(kmacro-tests-test-quit-counter-equal-01)
	(kmacro-tests-test-quit-counter-equal-02)
	(kmacro-tests-test-quit-counter-equal-03)
	(kmacro-tests-test-quit-counter-equal-04)
	(kmacro-tests-test-quit-counter-less)
	(kmacro-tests-test-quit-counter-greater):
	Implement unit tests for advanced keyboard macro counter
	commands.

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: Type: text/x-patch, Size: 640 bytes --]

diff --git a/doc/emacs/emacs.texi b/doc/emacs/emacs.texi
index 7071ea44edd..2584dce8d44 100644
--- a/doc/emacs/emacs.texi
+++ b/doc/emacs/emacs.texi
@@ -434,6 +434,7 @@ Top
 * Basic Keyboard Macro::     Defining and running keyboard macros.
 * Keyboard Macro Ring::      Where previous keyboard macros are saved.
 * Keyboard Macro Counter::   Inserting incrementing numbers in macros.
+* Advanced Macro Counter::   Advanced macro counter commands.
 * Keyboard Macro Query::     Making keyboard macros do different things each
                                 time.
 * Save Keyboard Macro::      Giving keyboard macros names; saving them in

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: Type: text/x-patch, Size: 5747 bytes --]

diff --git a/doc/emacs/kmacro.texi b/doc/emacs/kmacro.texi
index fc1402b489d..27c84c0f96f 100644
--- a/doc/emacs/kmacro.texi
+++ b/doc/emacs/kmacro.texi
@@ -35,6 +35,7 @@ Keyboard Macros
 * Basic Keyboard Macro::     Defining and running keyboard macros.
 * Keyboard Macro Ring::      Where previous keyboard macros are saved.
 * Keyboard Macro Counter::   Inserting incrementing numbers in macros.
+* Advanced Macro Counter::   Advanced macro counter commands.
 * Keyboard Macro Query::     Making keyboard macros do different things each
                                time.
 * Save Keyboard Macro::      Giving keyboard macros names; saving them in
@@ -364,6 +365,123 @@ Keyboard Macro Counter
 keyboard macro counter.  @xref{Number Registers}.  For most purposes,
 it is simpler to use a keyboard macro counter.
 
+@node Advanced Macro Counter
+@section Advanced Macro Counter Commands
+
+  The counter associated with a keyboard macro is sufficient in most
+cases.  If additional counters are required for a macro, registers can
+be used and these advanced macro counter commands simplify the
+interaction between the two.  Additional commands are provided to
+terminate a macro after a predefined number of runs.
+
+
+@table @kbd
+@item C-x C-k C-r l
+Load the value of a number register into the macro counter
+(@code{kmacro-reg-load-counter}).
+@item C-x C-k C-r s
+Save the value of the macro counter to a number register
+(@code{kmacro-reg-save-counter}).
+@end table
+
+@table @kbd
+@item C-x C-k C-r a =
+Compare if the macro counter is equal to the value of a register and
+increment the counter if it is (@code{kmacro-reg-add-counter-equal}).
+@item C-x C-k C-r a <
+Compare if the macro counter is less than the value of a register and
+increment the counter if it is (@code{kmacro-reg-add-counter-less}).
+@item C-x C-k C-r a >
+Compare if the macro counter is greater than the value of a register
+and increment the counter if it is
+(@code{kmacro-reg-add-counter-greater}).
+@end table
+
+@table @kbd
+@item C-x C-k C-q =
+Compare if the macro counter is equal to the prefix and terminate the
+macro if it is (@code{kmacro-quit-counter-equal}).
+@item C-x C-k C-q <
+Compare if the macro counter is less than the prefix and terminate the
+macro if it is (@code{kmacro-quit-counter-less}).
+@item C-x C-k C-q >
+Compare if the macro counter is greater than the prefix and terminate
+the macro if it is (@code{kmacro-quit-counter-greater}).
+@end table
+
+@findex kmacro-reg-load-counter
+@kindex C-x C-k C-r l
+@findex kmacro-reg-save-counter
+@kindex C-x C-k C-r s
+  The command @kbd{C-x C-k C-r l} (@code{kmacro-reg-load-counter})
+prompts for the register name from which to load a number into the
+macro counter.  The command @kbd{C-x C-k C-r s}
+(@code{kmacro-reg-save-counter}) prompts for the register name into
+which to save the macro counter's value.  Both @kbd{C-x C-k C-r l}
+(@code{kmacro-reg-load-counter}) and @kbd{C-x C-k C-r s}
+(@code{kmacro-reg-save-counter}) show a preview of the registers by
+default.  @xref{Registers}.  Both commands can be used during or
+outside a keyboard macro definition.
+
+@findex kmacro-reg-add-counter-equal
+@kindex C-x C-k C-r a =
+@findex kmacro-reg-add-counter-less
+@kindex C-x C-k C-r a <
+@findex kmacro-reg-add-counter-greater
+@kindex C-x C-k C-r a >
+  The @kbd{C-x C-k C-r a =} (@code{kmacro-reg-add-counter-equal}),
+@kbd{C-x C-k C-r a <} (@code{kmacro-reg-add-counter-less}), and
+@kbd{C-x C-k C-r a >} (@code{kmacro-reg-add-counter-greater}) commands
+all follow the same pattern.  During keyboard macro definition, the
+command prompts for a register name (with preview by default), the
+contents of which will be compared with the macro counter's value.  If
+the counter is equal to (@code{=}), less than (@code{<}), or greater
+than (@code{>}) the number register's contents, the counter will be
+incremented by the numeric prefix or one if no prefix was given to the
+command.
+
+  For example,
+
+@example
+C-u 2 C-x C-k C-r a > N
+@end example
+
+@noindent
+compares the counter with the contents of register @code{N} and if the
+counter is greater than that, increases it by two.
+
+@findex kmacro-quit-counter-equal
+@kindex C-x C-k C-q =
+@findex kmacro-quit-counter-less
+@kindex C-x C-k C-q <
+@findex kmacro-quit-counter-greater
+@kindex C-x C-k C-q >
+  Finally, the @kbd{C-x C-k C-q =} (@code{kmacro-quit-counter-equal}),
+@kbd{C-x C-k C-q <} (@code{kmacro-quit-counter-less}), and @kbd{C-x
+C-k C-q >} (@code{kmacro-quit-counter-greater}) commands compare the
+macro counter with the prefix given and terminate the execution of the
+macro, if the comparison succeeds.  If no numeric prefix or only
+@code{C-u} are given, the counter will be compared with zero.  The
+macro is terminated using the @code{keyboard-quit} function.  Using
+this command to exit from a macro that has been called by another
+macro is not supported, the entire executing macro is terminated.
+
+  The quit commands can be used to construct the equivalent of a
+@code{while}-loop. This example will stop after ten executions
+assuming the starting value for the macro counter is the default zero.
+
+@example
+C-u 10 C-x C-k C-q = C-x C-k C-i @key{RET}
+@end example
+
+  With the default counter value of zero, the macro called with a
+prefix of @code{C-u C-u} to execute sixteen times, will stop after ten
+iterations.  The counter values that have been inserted will be from 0
+to 9.  If the counter starts out a different value below ten, it will
+still stop at ten, because the counter does not actually count macro
+executions, but is incremented explicitly by the @code{C-x C-k C-i}
+command.
+
 @node Keyboard Macro Query
 @section Executing Macros with Variations

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #4: Type: text/x-patch, Size: 984 bytes --]

diff --git a/etc/NEWS b/etc/NEWS
index 4fbe09e0541..d5a3ebb1df5 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -187,6 +187,25 @@ This command adds a docstring comment to the current defun.  If a
 comment already exists, point is only moved to the comment.  It is
 bound to 'C-c C-d' in 'go-ts-mode'.
 
+** Kmacro
+
++++
+*** New Advanced Macro Counter functions.
+New commands have been added to to implement advanced macro counter
+functions.
+
+The commands 'C-x C-k C-r l' and 'C-x C-k C-r s' load and save the
+macro counter from a to a number register respectively.
+
+The commands 'C-x C-k C-r a =', 'C-x C-k C-r a <', and
+'C-x C-k C-r a >' compare the macro counter with the contents of a
+number register and increment the counter by a prefix if the
+comparison succeeds.
+
+The commands 'C-x C-k C-q =', 'C-x C-k C-q <', and 'C-x C-k C-q >'
+compare the macro counter with a prefix and terminate the macro if the
+comparison succeeds.
+
 \f
 * New Modes and Packages in Emacs 30.1
 

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #5: Type: text/x-patch, Size: 3911 bytes --]

diff --git a/lisp/kmacro.el b/lisp/kmacro.el
index 94d8794bd23..e7c3f75efd0 100644
--- a/lisp/kmacro.el
+++ b/lisp/kmacro.el
@@ -183,10 +183,18 @@ kmacro-keymap
   "C-l"  #'kmacro-call-ring-2nd-repeat
 
   ;; macro counter
-  "C-f"  #'kmacro-set-format
-  "C-c"  #'kmacro-set-counter
-  "C-i"  #'kmacro-insert-counter
-  "C-a"  #'kmacro-add-counter
+  "C-f"     #'kmacro-set-format
+  "C-c"     #'kmacro-set-counter
+  "C-i"     #'kmacro-insert-counter
+  "C-a"     #'kmacro-add-counter
+  "C-r l"   #'kmacro-reg-load-counter
+  "C-r s"   #'kmacro-reg-save-counter
+  "C-r a =" #'kmacro-reg-add-counter-equal
+  "C-r a <" #'kmacro-reg-add-counter-less
+  "C-r a >" #'kmacro-reg-add-counter-greater
+  "C-q ="   #'kmacro-quit-counter-equal
+  "C-q <"   #'kmacro-quit-counter-less
+  "C-q >"   #'kmacro-quit-counter-greater
 
   ;; macro editing
   "C-e"  #'kmacro-edit-macro-repeat
@@ -347,6 +355,89 @@ kmacro-add-counter
     (kmacro-display-counter)))
 
 
+(defun kmacro-reg-load-counter (register)
+  "Load the value of a register into `kmacro-counter'"
+  (interactive
+   (list (register-read-with-preview "Load register to counter: ")))
+  (let ((register-val (get-register register)))
+    (when (numberp register-val)
+     (setq kmacro-counter register-val))))
+
+
+(defun kmacro-reg-save-counter (register)
+  "Save the value of `kmacro-counter' to a register"
+  (interactive
+   (list (register-read-with-preview "Save counter to register: ")))
+  (set-register register kmacro-counter))
+
+
+(defun kmacro-reg-add-counter-equal (&optional arg)
+  "Increment counter by ARG if it is equal to register value"
+  (interactive "p")
+  (let
+      ((register (register-read-with-preview "Compare counter to register: ")))
+    (kmacro-reg-add-counter '= register arg)))
+
+
+(defun kmacro-reg-add-counter-less (&optional arg)
+  "Increment counter by ARG if it is less than register value"
+  (interactive "p")
+  (let
+      ((register (register-read-with-preview "Compare counter to register: ")))
+    (kmacro-reg-add-counter '< register arg)))
+
+
+(defun kmacro-reg-add-counter-greater (&optional arg)
+  "Increment counter by ARG if it is greater than register value"
+  (interactive "p")
+  (let
+      ((register (register-read-with-preview "Compare counter to register: ")))
+    (kmacro-reg-add-counter '> register arg)))
+
+
+(defun kmacro-reg-add-counter (func register &optional arg)
+  "Increment the counter by ARG if (FUNC kmacro-counter REGISTER-VALUE)
+is true.
+With no ARG, ARG is set to 1"
+  (let ((register-val (get-register register))
+        (arg (if (null arg) 1 arg)))
+    (when (apply func (list kmacro-counter register-val))
+      (setq current-prefix-arg nil)
+      (kmacro-add-counter arg))))
+
+
+(defun kmacro-quit-counter-equal (&optional arg)
+  "Quit the keyboard macro if the counter is equal to ARG"
+  (interactive "P")
+  (kmacro-quit-counter '= arg))
+
+
+(defun kmacro-quit-counter-less (&optional arg)
+  "Quit the keyboard macro if the counter is less than ARG"
+  (interactive "P")
+  (kmacro-quit-counter '< arg))
+
+
+(defun kmacro-quit-counter-greater (&optional arg)
+  "Quit the keyboard macro if the counter is greater than ARG"
+    (interactive "P")
+    (kmacro-quit-counter '> arg))
+
+
+(defun kmacro-quit-counter (func &optional arg)
+  "Quit the keyboard macro if (FUNC kmacro-counter ARG) is true.
+With \\[universal-argument] or no ARG, ARG is set to 0"
+  (when kmacro-initial-counter-value
+      (setq kmacro-counter kmacro-initial-counter-value
+	    kmacro-initial-counter-value nil))
+  (let ((arg
+	 (cond ((or (consp arg) (null arg)) 0)
+	       ((eq '- arg) -1)
+	       (t arg))))
+    (when (apply func (list kmacro-counter arg))
+      (keyboard-quit))))
+
+
 (defun kmacro-loop-setup-function ()
   "Function called prior to each iteration of macro."
   ;; Restore macro counter format to initial format, so it is ok to change

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #6: Type: text/x-patch, Size: 10913 bytes --]

diff --git a/test/lisp/kmacro-tests.el b/test/lisp/kmacro-tests.el
index 551fd8b60fc..e30681db539 100644
--- a/test/lisp/kmacro-tests.el
+++ b/test/lisp/kmacro-tests.el
@@ -275,6 +275,220 @@ kmacro-tests-start-insert-counter-appends-to-macro
     ;;  Verify that the recording state has changed.
     (should (equal defining-kbd-macro 'append))))
 
+
+(kmacro-tests-deftest kmacro-tests-test-reg-load ()
+  "`kmacro-reg-load-counter' loads the value of register to into the counter"
+  (set-register ?\C-r 4) ;; Should be safe as a register name
+  (kmacro-tests-simulate-command '(kmacro-set-counter 1))
+  (kmacro-tests-define-macro (vconcat
+                              ;; Insert and increment counter
+                              "\C-x\C-k\C-i"
+                              "\C-x\C-k\C-i"
+                              ;; Load from register
+                              "\C-x\C-k\C-rl\C-r"
+                              ))
+  (kmacro-tests-should-insert "1245"
+    (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 2)))
+    (set-register ?\C-r nil))
+
+(kmacro-tests-deftest kmacro-tests-test-reg-save ()
+  "`kmacro-reg-save-counter' save the counter to a register"
+  (set-register ?\C-r nil) ;; Should be safe as a register name
+  (kmacro-tests-simulate-command '(kmacro-set-counter 1))
+  (kmacro-tests-define-macro (vconcat
+                              ;; Insert and increment counter
+                              "\C-x\C-k\C-i"
+                              ;; Save to register
+                              "\C-x\C-k\C-rs\C-r"
+                              ;; Add to counter
+                              "\C-u2\C-x\C-k\C-a"
+                              ;; Insert and increment counter
+                              "\C-x\C-k\C-i"
+                              ;; Insert register
+                              "\C-xri\C-r"
+                              ))
+  (kmacro-tests-should-insert "142586"
+    (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 2)))
+  (set-register ?\C-r nil))
+
+
+(kmacro-tests-deftest kmacro-tests-test-reg-add-counter-equal-01 ()
+  "`kmacro-reg-add-counter-equal' increments counter if equal to register"
+  (set-register ?\C-r 2) ;; Should be safe as a register name
+  (kmacro-tests-define-macro (vconcat
+                              ;; Insert and increment counter
+                              "\C-x\C-k\C-i"
+                              ;; Increment counter if it matches
+                              "\C-x\C-k\C-ra=\C-r"
+                              ))
+  (kmacro-tests-should-insert "0134"
+    (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 4)))
+  (set-register ?\C-r nil))
+
+(kmacro-tests-deftest kmacro-tests-test-reg-add-counter-equal-02 ()
+  "`kmacro-reg-add-counter-equal' increments counter if equal to register"
+  (set-register ?\C-r 2) ;; Should be safe as a register name
+  (kmacro-tests-define-macro (vconcat
+                              ;; Insert and increment counter
+                              "\C-x\C-k\C-i"
+                              ;; Add two to counter if it matches
+                              "\C-u2\C-x\C-k\C-ra=\C-r"
+                              ))
+  (kmacro-tests-should-insert "0145"
+    (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 4)))
+  (set-register ?\C-r nil))
+
+(kmacro-tests-deftest kmacro-tests-test-reg-add-counter-equal-03 ()
+  "`kmacro-reg-add-counter-equal' increments counter if equal to register"
+  (set-register ?\C-r 2) ;; Should be safe as a register name
+  (kmacro-tests-define-macro (vconcat
+                              ;; Insert and increment counter
+                              "\C-x\C-k\C-i"
+                              ;; Add four to counter if it matches
+                              "\C-u\C-x\C-k\C-ra=\C-r"
+                              ))
+  (kmacro-tests-should-insert "0167"
+    (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 4)))
+  (set-register ?\C-r nil))
+
+(kmacro-tests-deftest kmacro-tests-test-reg-add-counter-equal-04 ()
+  "`kmacro-reg-add-counter-equal' increments counter if equal to register"
+  (set-register ?\C-r 2) ;; Should be safe as a register name
+  (kmacro-tests-define-macro (vconcat
+                              ;; Insert and increment counter
+                              "\C-x\C-k\C-i"
+                              ;; Decrement counter if it matches
+                              "\C-u-\C-x\C-k\C-ra=\C-r"
+                              ))
+  (kmacro-tests-should-insert "0111"
+    (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 4)))
+  (set-register ?\C-r nil))
+
+(kmacro-tests-deftest kmacro-tests-test-reg-add-counter-less ()
+  "`kmacro-reg-add-counter-less' increments counter if less than register"
+  (set-register ?\C-r 6) ;; Should be safe as a register name
+  (kmacro-tests-simulate-command '(kmacro-set-counter 8))
+  (kmacro-tests-define-macro (vconcat
+                              ;; Decrement counter if it's
+                              ;; less than the register
+                              "\C-u-1\C-x\C-k\C-ra<\C-r"
+                              ;; Insert and decrement counter
+                              "\C-u-\C-x\C-k\C-i"
+                              ))
+  (kmacro-tests-should-insert "8764"
+    (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 4)))
+    (set-register ?\C-r nil))
+
+(kmacro-tests-deftest kmacro-tests-test-reg-add-counter-greater ()
+  "`kmacro-reg-add-counter-greater' increments counter if greater than register"
+  (set-register ?\C-r 2) ;; Should be safe as a register name
+  (kmacro-tests-define-macro (vconcat
+                              ;; Insert and increment counter
+                              "\C-x\C-k\C-i"
+                              ;; Increment counter if it's greater
+                              ;; than the register
+                              "\C-x\C-k\C-ra>\C-r"
+                              ))
+  (kmacro-tests-should-insert "0124"
+    (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 4)))
+  (set-register ?\C-r nil))
+
+
+(kmacro-tests-deftest kmacro-tests-test-quit-counter-equal-01 ()
+  "`kmacro-quit-counter-equal' stops macro if counter is equal to prefix"
+  (kmacro-tests-simulate-command '(kmacro-set-counter 5))
+  (kmacro-tests-define-macro (vconcat
+                              ;; Insert and decrement counter
+                              "\C-u-\C-x\C-k\C-i"
+                              ;; Stop if the counter is at 0
+                              "\C-x\C-k\C-q="
+                              ))
+  (kmacro-tests-should-insert "5432"
+    (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 4)))
+  (should (condition-case abort
+              (should (= 1 kmacro-counter))
+              (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 1))
+            (quit abort))))
+
+(kmacro-tests-deftest kmacro-tests-test-quit-counter-equal-02 ()
+  "`kmacro-quit-counter-equal' stops macro if counter is equal to prefix"
+  (kmacro-tests-define-macro (vconcat
+                              ;; Insert and increment counter
+                              "\C-x\C-k\C-i"
+                              ;; Stop if the counter is at 5
+                              "\C-u5\C-x\C-k\C-q="
+                              ))
+  (kmacro-tests-should-insert "0123"
+    (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 4)))
+  (should (condition-case abort
+              (should (= 4 kmacro-counter))
+              (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 1))
+            (quit abort))))
+
+(kmacro-tests-deftest kmacro-tests-test-quit-counter-equal-03 ()
+  "`kmacro-quit-counter-equal' stops macro if counter is equal to prefix"
+  (kmacro-tests-simulate-command '(kmacro-set-counter 5))
+  (kmacro-tests-define-macro (vconcat
+                              ;; Insert and decrement counter
+                              "\C-u-\C-x\C-k\C-i"
+                              ;; Stop if the counter is at 0
+                              "\C-u\C-x\C-k\C-q="
+                              ))
+  (kmacro-tests-should-insert "5432"
+    (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 4)))
+  (should (condition-case abort
+              (should (= 1 kmacro-counter))
+              (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 1))
+            (quit abort))))
+
+(kmacro-tests-deftest kmacro-tests-test-quit-counter-equal-04 ()
+  "`kmacro-quit-counter-equal' stops macro if counter is equal to prefix"
+  (kmacro-tests-simulate-command '(kmacro-set-counter 4))
+  (kmacro-tests-define-macro (vconcat
+                              ;; Insert and decrement counter
+                              "\C-u-\C-x\C-k\C-i"
+                              ;; Stop if the counter is at -1
+                              "\C-u-\C-x\C-k\C-q="
+                              ))
+  (kmacro-tests-should-insert "4321"
+    (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 4)))
+  (should (condition-case abort
+              (should (= 0 kmacro-counter))
+              (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 1))
+            (quit abort))))
+
+(kmacro-tests-deftest kmacro-tests-test-quit-counter-less ()
+  "`kmacro-quit-counter-less' stops macro if counter is less than prefix"
+  (kmacro-tests-simulate-command '(kmacro-set-counter 8))
+  (kmacro-tests-define-macro (vconcat
+                              ;; Stop if the counter is less than 5
+                              "\C-u5\C-x\C-k\C-q<"
+                              ;; Insert and decrement counter
+                              "\C-u-\C-x\C-k\C-i"
+                              ))
+  (kmacro-tests-should-insert "8765"
+    (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 4)))
+  (should (condition-case abort
+              (should (= 4 kmacro-counter))
+              (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 1))
+            (quit abort))))
+
+(kmacro-tests-deftest kmacro-tests-test-quit-counter-greater ()
+  "`kmacro-quit-counter-greater' stops macro if counter is greater than prefix"
+  (kmacro-tests-define-macro (vconcat
+                              ;; Insert and increment counter
+                              "\C-x\C-k\C-i"
+                              ;; Stop if the counter is greater than 4
+                              "\C-u4\C-x\C-k\C-q>"
+                              ))
+  (kmacro-tests-should-insert "0123"
+    (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 4)))
+  (should (condition-case abort
+              (should (= 4 kmacro-counter))
+              (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 1))
+            (quit abort))))
+
+
 (kmacro-tests-deftest kmacro-tests-end-call-macro-prefix-args ()
   "kmacro-end-call-macro changes behavior based on prefix arg."
   ;; "Record" two macros.

[-- Attachment #7: Type: text/plain, Size: 10 bytes --]

-- 
Alex.

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

* bug#61549: 30.0.50; [PATCH] New keyboard macro counter functions
  2023-02-16  8:17 bug#61549: 30.0.50; [PATCH] New keyboard macro counter functions Alex Bochannek
@ 2023-02-17  8:13 ` Eli Zaretskii
  2023-02-19  1:59   ` Alex Bochannek
  0 siblings, 1 reply; 22+ messages in thread
From: Eli Zaretskii @ 2023-02-17  8:13 UTC (permalink / raw)
  To: Alex Bochannek, Stefan Monnier, Lars Ingebrigtsen; +Cc: 61549

> From: Alex Bochannek <alex@bochannek.com>
> Date: Thu, 16 Feb 2023 00:17:25 -0800
> 
> I have been working on blog posts about keyboard macros and found that
> it would be useful to have comparison functions for the keyboard macro
> counter.

Thanks.

> I implemented two functions to load and save macro counter values from
> and to number registers; three comparison functions of the macro counter
> with a number register that conditionally increment the counter; three
> comparison functions of the macro counter with a prefix that terminate
> the macro execution. This simplifies handling multiple counters and
> conditional macro termination.
> 
> I am attaching the changes to:
>   emacs.texi
>   kmacro.texi
>   NEWS
>   kmacro.el
>   kmacro-tests.el
> 
> I hope this functionality is useful and that I followed the coding and
> style standards.

I wonder whether these commands are important enough to have them in
the manual.  Stefan and Lars, WDYT?  Any other comments to the feature
and its implementation?

> +(defun kmacro-reg-load-counter (register)
> +  "Load the value of a register into `kmacro-counter'"

The first line of a doc string should be a single complete sentence,
ending with a period (here and elsewhere).  You may wish running
checkdoc on your code to reveal any issues.

> +(defun kmacro-reg-add-counter-equal (&optional arg)
> +  "Increment counter by ARG if it is equal to register value"

This doc string is confusing, I think.  Would you like to reword it to
clarify whet the command does?  In particular, the "it" part is
ambiguous.

> +(defun kmacro-reg-add-counter-less (&optional arg)
> +  "Increment counter by ARG if it is less than register value"
> +  (interactive "p")
> +  (let
> +      ((register (register-read-with-preview "Compare counter to register: ")))
> +    (kmacro-reg-add-counter '< register arg)))
> +
> +
> +(defun kmacro-reg-add-counter-greater (&optional arg)
> +  "Increment counter by ARG if it is greater than register value"
> +  (interactive "p")
> +  (let
> +      ((register (register-read-with-preview "Compare counter to register: ")))
> +    (kmacro-reg-add-counter '> register arg)))

Similar problems with the doc strings of these two commands.

> +(defun kmacro-reg-add-counter (func register &optional arg)
> +  "Increment the counter by ARG if (FUNC kmacro-counter REGISTER-VALUE)
> +is true.
> +With no ARG, ARG is set to 1"

Our style is to say "ARG is the numeric prefix argument that defaults
to 1."

> +(defun kmacro-quit-counter-equal (&optional arg)
> +  "Quit the keyboard macro if the counter is equal to ARG"

"when the counter is equal to ARG", I guess?

> +(defun kmacro-quit-counter-less (&optional arg)
> +  "Quit the keyboard macro if the counter is less than ARG"
> +  (interactive "P")
> +  (kmacro-quit-counter '< arg))
> +
> +
> +(defun kmacro-quit-counter-greater (&optional arg)
> +  "Quit the keyboard macro if the counter is greater than ARG"
> +    (interactive "P")
> +    (kmacro-quit-counter '> arg))

Likewise here.

> +(defun kmacro-quit-counter (func &optional arg)
> +  "Quit the keyboard macro if (FUNC kmacro-counter ARG) is true.

Our style is to use PRED instead of FUNC, and document like this:

    Quit the keyboard macro when predicate PRED returns non-nil.
  PRED is called with two arguments: kmacro-counter and ARG.

> +With \\[universal-argument] or no ARG, ARG is set to 0"

  "Arg is the prefix numeric argument and defaults to zero."

> +  (let ((arg
> +	 (cond ((or (consp arg) (null arg)) 0)
> +	       ((eq '- arg) -1)
> +	       (t arg))))

This seems to imply that ARG has meaning beyond what the above text
says.





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

* bug#61549: 30.0.50; [PATCH] New keyboard macro counter functions
  2023-02-17  8:13 ` Eli Zaretskii
@ 2023-02-19  1:59   ` Alex Bochannek
  2023-02-19  6:54     ` Eli Zaretskii
  0 siblings, 1 reply; 22+ messages in thread
From: Alex Bochannek @ 2023-02-19  1:59 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: Lars Ingebrigtsen, Stefan Monnier, 61549

Eli,

Thanks for all the doc string comments, I was not sure about them and I
don't think I knew about checkdoc. I should spend some more time with
Appendix D of the Elisp manual.

Eli Zaretskii <eliz@gnu.org> writes:

>> +  (let ((arg
>> +	 (cond ((or (consp arg) (null arg)) 0)
>> +	       ((eq '- arg) -1)
>> +	       (t arg))))
>
> This seems to imply that ARG has meaning beyond what the above text
> says.

I was struggling a bit with this one. On the one hand, I wanted the raw
prefix because I didn't want `C-u' to turn into 4, on the other hand, I
still wanted to let `C-u -' be interpreted as -1. Is there a better way
to do this?

I am updating the doc strings and submit a new version in the next few
days.

Thanks for the review!

-- 
Alex.





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

* bug#61549: 30.0.50; [PATCH] New keyboard macro counter functions
  2023-02-19  1:59   ` Alex Bochannek
@ 2023-02-19  6:54     ` Eli Zaretskii
  2023-03-06  3:37       ` Alex Bochannek
  0 siblings, 1 reply; 22+ messages in thread
From: Eli Zaretskii @ 2023-02-19  6:54 UTC (permalink / raw)
  To: Alex Bochannek; +Cc: larsi, monnier, 61549

> From: Alex Bochannek <alex@bochannek.com>
> Cc: Stefan Monnier <monnier@iro.umontreal.ca>,  Lars Ingebrigtsen
>  <larsi@gnus.org>,  61549@debbugs.gnu.org
> Date: Sat, 18 Feb 2023 17:59:15 -0800
> 
> >> +  (let ((arg
> >> +	 (cond ((or (consp arg) (null arg)) 0)
> >> +	       ((eq '- arg) -1)
> >> +	       (t arg))))
> >
> > This seems to imply that ARG has meaning beyond what the above text
> > says.
> 
> I was struggling a bit with this one. On the one hand, I wanted the raw
> prefix because I didn't want `C-u' to turn into 4, on the other hand, I
> still wanted to let `C-u -' be interpreted as -1. Is there a better way
> to do this?

Why do you want C-u to be equivalent to "C-u 0"?  That's inconsistent,
and you already provide an easy way of supplying zero: by using no
prefix argument at all.

In any case, what I meant was that the processing is incompletely
described by the documentation.  And the documentation about the
effect of the prefix argument should be in the doc strings of the
commands, not in the doc string of this function, which is not
intended for interactive use.





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

* bug#61549: 30.0.50; [PATCH] New keyboard macro counter functions
  2023-02-19  6:54     ` Eli Zaretskii
@ 2023-03-06  3:37       ` Alex Bochannek
  2023-03-11  8:49         ` Eli Zaretskii
                           ` (2 more replies)
  0 siblings, 3 replies; 22+ messages in thread
From: Alex Bochannek @ 2023-03-06  3:37 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: larsi, monnier, 61549

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

Eli,

Took me a little bit longer to find time to do this and I now have
incorporated your feedback in the below patch. Thank you for your
perspective on prefix, that made a lot of sense and I reworked that part
of the code to be consistent with how it usually works. I was not aware
that the interactive code `p' defaults to 1 in the absence of a prefix.
I couldn't find a place where this is documented and it simplified the
code.

I updated the docstrings as you suggested and even though checkdoc
complained about the lack of a period on the first line, I figured it's
better to keep below the line length limits.

Let me know if you would like to see any other changes, I always
appreciate constructive feedback!

I am attaching the changes to:
  kmacro.texi
  kmacro.el
  kmacro-tests.el

The changelog as well as NEWS and emacs.texi remain the same from my
original message.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: Type: text/x-patch, Size: 5729 bytes --]

diff --git a/doc/emacs/kmacro.texi b/doc/emacs/kmacro.texi
index fc1402b489d..c3d74bb39ed 100644
--- a/doc/emacs/kmacro.texi
+++ b/doc/emacs/kmacro.texi
@@ -35,6 +35,7 @@ Keyboard Macros
 * Basic Keyboard Macro::     Defining and running keyboard macros.
 * Keyboard Macro Ring::      Where previous keyboard macros are saved.
 * Keyboard Macro Counter::   Inserting incrementing numbers in macros.
+* Advanced Macro Counter::   Advanced macro counter commands.
 * Keyboard Macro Query::     Making keyboard macros do different things each
                                time.
 * Save Keyboard Macro::      Giving keyboard macros names; saving them in
@@ -364,6 +365,123 @@ Keyboard Macro Counter
 keyboard macro counter.  @xref{Number Registers}.  For most purposes,
 it is simpler to use a keyboard macro counter.
 
+@node Advanced Macro Counter
+@section Advanced Macro Counter Commands
+
+  The counter associated with a keyboard macro is sufficient in most
+cases.  If additional counters are required for a macro, registers can
+be used and these advanced macro counter commands simplify the
+interaction between the two.  Additional commands are provided to
+terminate a macro after a predefined number of runs.
+
+
+@table @kbd
+@item C-x C-k C-r l
+Load the value of a number register into the macro counter
+(@code{kmacro-reg-load-counter}).
+@item C-x C-k C-r s
+Save the value of the macro counter to a number register
+(@code{kmacro-reg-save-counter}).
+@end table
+
+@table @kbd
+@item C-x C-k C-r a =
+Compare if the macro counter is equal to the value of a register and
+increment the counter if it is (@code{kmacro-reg-add-counter-equal}).
+@item C-x C-k C-r a <
+Compare if the macro counter is less than the value of a register and
+increment the counter if it is (@code{kmacro-reg-add-counter-less}).
+@item C-x C-k C-r a >
+Compare if the macro counter is greater than the value of a register
+and increment the counter if it is
+(@code{kmacro-reg-add-counter-greater}).
+@end table
+
+@table @kbd
+@item C-x C-k C-q =
+Compare if the macro counter is equal to the prefix and terminate the
+macro if it is (@code{kmacro-quit-counter-equal}).
+@item C-x C-k C-q <
+Compare if the macro counter is less than the prefix and terminate the
+macro if it is (@code{kmacro-quit-counter-less}).
+@item C-x C-k C-q >
+Compare if the macro counter is greater than the prefix and terminate
+the macro if it is (@code{kmacro-quit-counter-greater}).
+@end table
+
+@findex kmacro-reg-load-counter
+@kindex C-x C-k C-r l
+@findex kmacro-reg-save-counter
+@kindex C-x C-k C-r s
+  The command @kbd{C-x C-k C-r l} (@code{kmacro-reg-load-counter})
+prompts for the register name from which to load a number into the
+macro counter.  The command @kbd{C-x C-k C-r s}
+(@code{kmacro-reg-save-counter}) prompts for the register name into
+which to save the macro counter's value.  Both @kbd{C-x C-k C-r l}
+(@code{kmacro-reg-load-counter}) and @kbd{C-x C-k C-r s}
+(@code{kmacro-reg-save-counter}) show a preview of the registers by
+default.  @xref{Registers}.  Both commands can be used during or
+outside a keyboard macro definition.
+
+@findex kmacro-reg-add-counter-equal
+@kindex C-x C-k C-r a =
+@findex kmacro-reg-add-counter-less
+@kindex C-x C-k C-r a <
+@findex kmacro-reg-add-counter-greater
+@kindex C-x C-k C-r a >
+  The @kbd{C-x C-k C-r a =} (@code{kmacro-reg-add-counter-equal}),
+@kbd{C-x C-k C-r a <} (@code{kmacro-reg-add-counter-less}), and
+@kbd{C-x C-k C-r a >} (@code{kmacro-reg-add-counter-greater}) commands
+all follow the same pattern.  During keyboard macro definition, the
+command prompts for a register name (with preview by default), the
+contents of which will be compared with the macro counter's value.  If
+the counter is equal to (@code{=}), less than (@code{<}), or greater
+than (@code{>}) the number register's contents, the counter will be
+incremented by the numeric prefix or one if no prefix was given to the
+command.
+
+  For example,
+
+@example
+C-u 2 C-x C-k C-r a > N
+@end example
+
+@noindent
+compares the counter with the contents of register @code{N} and if the
+counter is greater than that, increases it by two.
+
+@findex kmacro-quit-counter-equal
+@kindex C-x C-k C-q =
+@findex kmacro-quit-counter-less
+@kindex C-x C-k C-q <
+@findex kmacro-quit-counter-greater
+@kindex C-x C-k C-q >
+  Finally, the @kbd{C-x C-k C-q =} (@code{kmacro-quit-counter-equal}),
+@kbd{C-x C-k C-q <} (@code{kmacro-quit-counter-less}), and @kbd{C-x
+C-k C-q >} (@code{kmacro-quit-counter-greater}) commands compare the
+macro counter with the prefix given and terminate the execution of the
+macro, if the comparison succeeds.  If no numeric prefix is given, the
+counter will be compared with zero.  The macro is terminated using the
+@code{keyboard-quit} function.  Using this command to exit from a
+macro that has been called by another macro is not supported, the
+entire executing macro is terminated.
+
+  The quit commands can be used to construct the equivalent of a
+@code{while}-loop. This example will stop after ten executions
+assuming the starting value for the macro counter is the default zero.
+
+@example
+C-u 10 C-x C-k C-q = C-x C-k C-i @key{RET}
+@end example
+
+  With the default counter value of zero, the macro called with a
+prefix of @code{C-u C-u} to execute sixteen times, will stop after ten
+iterations.  The counter values that have been inserted will be from 0
+to 9.  If the counter starts out a different value below ten, it will
+still stop at ten, because the counter does not actually count macro
+executions, but is incremented explicitly by the @code{C-x C-k C-i}
+command.
+
 @node Keyboard Macro Query
 @section Executing Macros with Variations
 

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: Type: text/x-patch, Size: 4310 bytes --]

diff --git a/lisp/kmacro.el b/lisp/kmacro.el
index 94d8794bd23..d0630a1998f 100644
--- a/lisp/kmacro.el
+++ b/lisp/kmacro.el
@@ -183,10 +183,18 @@ kmacro-keymap
   "C-l"  #'kmacro-call-ring-2nd-repeat
 
   ;; macro counter
-  "C-f"  #'kmacro-set-format
-  "C-c"  #'kmacro-set-counter
-  "C-i"  #'kmacro-insert-counter
-  "C-a"  #'kmacro-add-counter
+  "C-f"     #'kmacro-set-format
+  "C-c"     #'kmacro-set-counter
+  "C-i"     #'kmacro-insert-counter
+  "C-a"     #'kmacro-add-counter
+  "C-r l"   #'kmacro-reg-load-counter
+  "C-r s"   #'kmacro-reg-save-counter
+  "C-r a =" #'kmacro-reg-add-counter-equal
+  "C-r a <" #'kmacro-reg-add-counter-less
+  "C-r a >" #'kmacro-reg-add-counter-greater
+  "C-q ="   #'kmacro-quit-counter-equal
+  "C-q <"   #'kmacro-quit-counter-less
+  "C-q >"   #'kmacro-quit-counter-greater
 
   ;; macro editing
   "C-e"  #'kmacro-edit-macro-repeat
@@ -347,6 +355,96 @@ kmacro-add-counter
     (kmacro-display-counter)))
 
 
+(defun kmacro-reg-load-counter (register)
+  "Load the value of REGISTER into `kmacro-counter'."
+  (interactive
+   (list (register-read-with-preview "Load register to counter: ")))
+  (let ((register-val (get-register register)))
+    (when (numberp register-val)
+     (setq kmacro-counter register-val))))
+
+
+(defun kmacro-reg-save-counter (register)
+  "Save the value of `kmacro-counter' to REGISTER."
+  (interactive
+   (list (register-read-with-preview "Save counter to register: ")))
+  (set-register register kmacro-counter))
+
+
+(defun kmacro-reg-add-counter-equal (&optional arg)
+  "Increment `kmacro-counter' by ARG if the counter is equal to a
+register's value.
+ARG is the numeric prefix argument that defaults to one."
+  (interactive "p")
+  (let
+      ((register (register-read-with-preview "Compare counter to register: ")))
+    (kmacro-reg-add-counter '= register arg)))
+
+
+(defun kmacro-reg-add-counter-less (&optional arg)
+  "Increment `kmacro-counter' by ARG if the counter is less than a
+register's value.
+ARG is the numeric prefix argument that defaults to one."
+  (interactive "p")
+  (let
+      ((register (register-read-with-preview "Compare counter to register: ")))
+    (kmacro-reg-add-counter '< register arg)))
+
+
+(defun kmacro-reg-add-counter-greater (&optional arg)
+  "Increment `kmacro-counter' by ARG if the counter is greater than
+a register's value.
+ARG is the numeric prefix argument that defaults to one."
+  (interactive "p")
+  (let
+      ((register (register-read-with-preview "Compare counter to register: ")))
+    (kmacro-reg-add-counter '> register arg)))
+
+
+(defun kmacro-reg-add-counter (pred register arg)
+  "Increment `kmacro-counter' by ARG if predicate PRED returns
+non-nil.
+PRED is called with two arguments: `kmacro-counter' and REGISTER."
+  (let ((register-val (get-register register)))
+    (when (apply pred (list kmacro-counter register-val))
+      (setq current-prefix-arg nil)
+      (kmacro-add-counter arg))))
+
+
+(defun kmacro-quit-counter-equal (&optional arg)
+  "Quit the keyboard macro if `kmacro-counter' is equal to ARG.
+ARG is the numeric prefix argument that defaults to zero."
+  (interactive "P")
+  (kmacro-quit-counter '= arg))
+
+
+(defun kmacro-quit-counter-less (&optional arg)
+  "Quit the keyboard macro if `kmacro-counter' is less than ARG.
+ARG is the numeric prefix argument that defaults to zero."
+  (interactive "P")
+  (kmacro-quit-counter '< arg))
+
+
+(defun kmacro-quit-counter-greater (&optional arg)
+  "Quit the keyboard macro if `kmacro-counter' is greater than ARG.
+ARG is the numeric prefix argument that defaults to zero."
+    (interactive "P")
+    (kmacro-quit-counter '> arg))
+
+
+(defun kmacro-quit-counter (pred &optional arg)
+  "Quit the keyboard macro if predicate PRED returns non-nil.
+PRED is called with two arguments: `kmacro-counter' and ARG."
+  (when kmacro-initial-counter-value
+    (setq kmacro-counter kmacro-initial-counter-value
+	  kmacro-initial-counter-value nil))
+  (let ((arg
+	 (cond ((null arg) 0)
+	       (t (prefix-numeric-value arg)))))
+    (when (apply pred (list kmacro-counter arg))
+      (keyboard-quit))))
+
+
 (defun kmacro-loop-setup-function ()
   "Function called prior to each iteration of macro."
   ;; Restore macro counter format to initial format, so it is ok to change

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #4: Type: text/x-patch, Size: 10111 bytes --]

diff --git a/test/lisp/kmacro-tests.el b/test/lisp/kmacro-tests.el
index 551fd8b60fc..42ada9f4f94 100644
--- a/test/lisp/kmacro-tests.el
+++ b/test/lisp/kmacro-tests.el
@@ -275,6 +275,205 @@ kmacro-tests-start-insert-counter-appends-to-macro
     ;;  Verify that the recording state has changed.
     (should (equal defining-kbd-macro 'append))))
 
+
+(kmacro-tests-deftest kmacro-tests-test-reg-load ()
+  "`kmacro-reg-load-counter' loads the value of register into counter"
+  (set-register ?\C-r 4) ;; Should be safe as a register name
+  (kmacro-tests-simulate-command '(kmacro-set-counter 1))
+  (kmacro-tests-define-macro (vconcat
+                              ;; Insert and increment counter
+                              "\C-x\C-k\C-i"
+                              "\C-x\C-k\C-i"
+                              ;; Load from register
+                              "\C-x\C-k\C-rl\C-r"
+                              ))
+  (kmacro-tests-should-insert "1245"
+    (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 2)))
+    (set-register ?\C-r nil))
+
+(kmacro-tests-deftest kmacro-tests-test-reg-save ()
+  "`kmacro-reg-save-counter' saves counter to register"
+  (set-register ?\C-r nil) ;; Should be safe as a register name
+  (kmacro-tests-simulate-command '(kmacro-set-counter 1))
+  (kmacro-tests-define-macro (vconcat
+                              ;; Insert and increment counter
+                              "\C-x\C-k\C-i"
+                              ;; Save to register
+                              "\C-x\C-k\C-rs\C-r"
+                              ;; Add to counter
+                              "\C-u2\C-x\C-k\C-a"
+                              ;; Insert and increment counter
+                              "\C-x\C-k\C-i"
+                              ;; Insert register
+                              "\C-xri\C-r"
+                              ))
+  (kmacro-tests-should-insert "142586"
+    (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 2)))
+  (set-register ?\C-r nil))
+
+
+(kmacro-tests-deftest kmacro-tests-test-reg-add-counter-equal-01 ()
+  "`kmacro-reg-add-counter-equal' increments counter if equal to register"
+  (set-register ?\C-r 2) ;; Should be safe as a register name
+  (kmacro-tests-define-macro (vconcat
+                              ;; Insert and increment counter
+                              "\C-x\C-k\C-i"
+                              ;; Increment counter if it matches
+                              "\C-x\C-k\C-ra=\C-r"
+                              ))
+  (kmacro-tests-should-insert "0134"
+    (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 4)))
+  (set-register ?\C-r nil))
+
+(kmacro-tests-deftest kmacro-tests-test-reg-add-counter-equal-02 ()
+  "`kmacro-reg-add-counter-equal' increments counter if equal to register"
+  (set-register ?\C-r 2) ;; Should be safe as a register name
+  (kmacro-tests-define-macro (vconcat
+                              ;; Insert and increment counter
+                              "\C-x\C-k\C-i"
+                              ;; Add two to counter if it matches
+                              "\C-u2\C-x\C-k\C-ra=\C-r"
+                              ))
+  (kmacro-tests-should-insert "0145"
+    (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 4)))
+  (set-register ?\C-r nil))
+
+(kmacro-tests-deftest kmacro-tests-test-reg-add-counter-equal-03 ()
+  "`kmacro-reg-add-counter-equal' increments counter if equal to register"
+  (set-register ?\C-r 2) ;; Should be safe as a register name
+  (kmacro-tests-define-macro (vconcat
+                              ;; Insert and increment counter
+                              "\C-x\C-k\C-i"
+                              ;; Add four to counter if it matches
+                              "\C-u\C-x\C-k\C-ra=\C-r"
+                              ))
+  (kmacro-tests-should-insert "0167"
+    (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 4)))
+  (set-register ?\C-r nil))
+
+(kmacro-tests-deftest kmacro-tests-test-reg-add-counter-equal-04 ()
+  "`kmacro-reg-add-counter-equal' increments counter if equal to register"
+  (set-register ?\C-r 2) ;; Should be safe as a register name
+  (kmacro-tests-define-macro (vconcat
+                              ;; Insert and increment counter
+                              "\C-x\C-k\C-i"
+                              ;; Decrement counter if it matches
+                              "\C-u-\C-x\C-k\C-ra=\C-r"
+                              ))
+  (kmacro-tests-should-insert "0111"
+    (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 4)))
+  (set-register ?\C-r nil))
+
+(kmacro-tests-deftest kmacro-tests-test-reg-add-counter-less ()
+  "`kmacro-reg-add-counter-less' increments counter if less than register"
+  (set-register ?\C-r 6) ;; Should be safe as a register name
+  (kmacro-tests-simulate-command '(kmacro-set-counter 8))
+  (kmacro-tests-define-macro (vconcat
+                              ;; Decrement counter if it's
+                              ;; less than the register
+                              "\C-u-1\C-x\C-k\C-ra<\C-r"
+                              ;; Insert and decrement counter
+                              "\C-u-\C-x\C-k\C-i"
+                              ))
+  (kmacro-tests-should-insert "8764"
+    (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 4)))
+    (set-register ?\C-r nil))
+
+(kmacro-tests-deftest kmacro-tests-test-reg-add-counter-greater ()
+  "`kmacro-reg-add-counter-greater' increments counter if greater than register"
+  (set-register ?\C-r 2) ;; Should be safe as a register name
+  (kmacro-tests-define-macro (vconcat
+                              ;; Insert and increment counter
+                              "\C-x\C-k\C-i"
+                              ;; Increment counter if it's greater
+                              ;; than the register
+                              "\C-x\C-k\C-ra>\C-r"
+                              ))
+  (kmacro-tests-should-insert "0124"
+    (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 4)))
+  (set-register ?\C-r nil))
+
+
+(kmacro-tests-deftest kmacro-tests-test-quit-counter-equal-01 ()
+  "`kmacro-quit-counter-equal' stops macro if counter is equal to prefix"
+  (kmacro-tests-simulate-command '(kmacro-set-counter 5))
+  (kmacro-tests-define-macro (vconcat
+                              ;; Insert and decrement counter
+                              "\C-u-\C-x\C-k\C-i"
+                              ;; Stop if the counter is at 0
+                              "\C-x\C-k\C-q="
+                              ))
+  (kmacro-tests-should-insert "5432"
+    (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 4)))
+  (should (condition-case abort
+              (should (= 1 kmacro-counter))
+              (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 1))
+            (quit abort))))
+
+(kmacro-tests-deftest kmacro-tests-test-quit-counter-equal-02 ()
+  "`kmacro-quit-counter-equal' stops macro if counter is equal to prefix"
+  (kmacro-tests-define-macro (vconcat
+                              ;; Insert and increment counter
+                              "\C-x\C-k\C-i"
+                              ;; Stop if the counter is at 5
+                              "\C-u5\C-x\C-k\C-q="
+                              ))
+  (kmacro-tests-should-insert "0123"
+    (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 4)))
+  (should (condition-case abort
+              (should (= 4 kmacro-counter))
+              (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 1))
+            (quit abort))))
+
+
+(kmacro-tests-deftest kmacro-tests-test-quit-counter-equal-03 ()
+  "`kmacro-quit-counter-equal' stops macro if counter is equal to prefix"
+  (kmacro-tests-simulate-command '(kmacro-set-counter 4))
+  (kmacro-tests-define-macro (vconcat
+                              ;; Insert and decrement counter
+                              "\C-u-\C-x\C-k\C-i"
+                              ;; Stop if the counter is at -1
+                              "\C-u-\C-x\C-k\C-q="
+                              ))
+  (kmacro-tests-should-insert "4321"
+    (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 4)))
+  (should (condition-case abort
+              (should (= 0 kmacro-counter))
+              (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 1))
+            (quit abort))))
+
+(kmacro-tests-deftest kmacro-tests-test-quit-counter-less ()
+  "`kmacro-quit-counter-less' stops macro if counter is less than prefix"
+  (kmacro-tests-simulate-command '(kmacro-set-counter 8))
+  (kmacro-tests-define-macro (vconcat
+                              ;; Stop if the counter is less than 5
+                              "\C-u5\C-x\C-k\C-q<"
+                              ;; Insert and decrement counter
+                              "\C-u-\C-x\C-k\C-i"
+                              ))
+  (kmacro-tests-should-insert "8765"
+    (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 4)))
+  (should (condition-case abort
+              (should (= 4 kmacro-counter))
+              (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 1))
+            (quit abort))))
+
+(kmacro-tests-deftest kmacro-tests-test-quit-counter-greater ()
+  "`kmacro-quit-counter-greater' stops macro if counter is greater than prefix"
+  (kmacro-tests-define-macro (vconcat
+                              ;; Insert and increment counter
+                              "\C-x\C-k\C-i"
+                              ;; Stop if the counter is greater than 4
+                              "\C-u4\C-x\C-k\C-q>"
+                              ))
+  (kmacro-tests-should-insert "0123"
+    (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 4)))
+  (should (condition-case abort
+              (should (= 4 kmacro-counter))
+              (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 1))
+            (quit abort))))
+
+
 (kmacro-tests-deftest kmacro-tests-end-call-macro-prefix-args ()
   "kmacro-end-call-macro changes behavior based on prefix arg."
   ;; "Record" two macros.

[-- Attachment #5: Type: text/plain, Size: 20 bytes --]


Thanks!

-- 
Alex.

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

* bug#61549: 30.0.50; [PATCH] New keyboard macro counter functions
  2023-03-06  3:37       ` Alex Bochannek
@ 2023-03-11  8:49         ` Eli Zaretskii
  2023-03-12  0:19         ` Michael Heerdegen
  2024-05-22 23:57         ` Alex Bochannek
  2 siblings, 0 replies; 22+ messages in thread
From: Eli Zaretskii @ 2023-03-11  8:49 UTC (permalink / raw)
  To: Alex Bochannek; +Cc: larsi, monnier, 61549

> From: Alex Bochannek <alex@bochannek.com>
> Cc: monnier@iro.umontreal.ca,  larsi@gnus.org,  61549@debbugs.gnu.org
> Date: Sun, 05 Mar 2023 19:37:21 -0800
> 
> Took me a little bit longer to find time to do this and I now have
> incorporated your feedback in the below patch. Thank you for your
> perspective on prefix, that made a lot of sense and I reworked that part
> of the code to be consistent with how it usually works. I was not aware
> that the interactive code `p' defaults to 1 in the absence of a prefix.
> I couldn't find a place where this is documented and it simplified the
> code.
> 
> I updated the docstrings as you suggested and even though checkdoc
> complained about the lack of a period on the first line, I figured it's
> better to keep below the line length limits.

Thanks, a few more comments below.

> Let me know if you would like to see any other changes, I always
> appreciate constructive feedback!
> 
> I am attaching the changes to:
>   kmacro.texi
>   kmacro.el
>   kmacro-tests.el
> 
> The changelog as well as NEWS and emacs.texi remain the same from my
> original message.

Stefan and Lars didn't respond, and I tend to think there's no need to
describe these functions in the Emacs user manual.  So for the next
iteration (which hopefully will be the last), please submit the patch
without the changes in the manual.  Also, please post all of the
other changes, including NEWS and the commit log message (and mention
the bug number in the latter).

> +(defun kmacro-reg-add-counter-equal (&optional arg)
> +  "Increment `kmacro-counter' by ARG if the counter is equal to a
> +register's value.

The first line of a doc string must be a complete sentence.

> +(defun kmacro-reg-add-counter-less (&optional arg)
> +  "Increment `kmacro-counter' by ARG if the counter is less than a
> +register's value.

Likewise here (and elsewhere in the patch).

> +ARG is the numeric prefix argument that defaults to one."
> +  (interactive "p")
> +  (let
> +      ((register (register-read-with-preview "Compare counter to register: ")))
> +    (kmacro-reg-add-counter '< register arg)))
> +
> +
> +(defun kmacro-reg-add-counter-greater (&optional arg)

I noticed that you always leave 2 empty lines between functions.  Is
that intentional?  We generally leave just one empty line.

> +(defun kmacro-reg-add-counter (pred register arg)
> +  "Increment `kmacro-counter' by ARG if predicate PRED returns
> +non-nil.
> +PRED is called with two arguments: `kmacro-counter' and REGISTER."
> +  (let ((register-val (get-register register)))
> +    (when (apply pred (list kmacro-counter register-val))
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
To avoid consing a list here, would it be better to use funcall
instead of apply here?

> +(defun kmacro-quit-counter (pred &optional arg)
> +  "Quit the keyboard macro if predicate PRED returns non-nil.
> +PRED is called with two arguments: `kmacro-counter' and ARG."
> +  (when kmacro-initial-counter-value
> +    (setq kmacro-counter kmacro-initial-counter-value
> +	  kmacro-initial-counter-value nil))
> +  (let ((arg
> +	 (cond ((null arg) 0)
> +	       (t (prefix-numeric-value arg)))))
> +    (when (apply pred (list kmacro-counter arg))
> +      (keyboard-quit))))

Likewise here.





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

* bug#61549: 30.0.50; [PATCH] New keyboard macro counter functions
  2023-03-06  3:37       ` Alex Bochannek
  2023-03-11  8:49         ` Eli Zaretskii
@ 2023-03-12  0:19         ` Michael Heerdegen
  2024-05-22 23:57         ` Alex Bochannek
  2 siblings, 0 replies; 22+ messages in thread
From: Michael Heerdegen @ 2023-03-12  0:19 UTC (permalink / raw)
  To: Alex Bochannek; +Cc: Eli Zaretskii, larsi, 61549, monnier

Alex Bochannek <alex@bochannek.com> writes:

> diff --git a/doc/emacs/kmacro.texi b/doc/emacs/kmacro.texi

Two small notes from me in addition to Eli's review:

> +(defun kmacro-reg-add-counter-equal (&optional arg)
> +  "Increment `kmacro-counter' by ARG if the counter is equal to a
> +register's value.
> +ARG is the numeric prefix argument that defaults to one."
> +  (interactive "p")
> +  (let
> +      ((register (register-read-with-preview "Compare counter to register: ")))
> +    (kmacro-reg-add-counter '= register arg)))
                               ^^
I think we should function quote here; similar in most of the other
functions.

> +(defun kmacro-quit-counter-equal (&optional arg)
> +  "Quit the keyboard macro if `kmacro-counter' is equal to ARG.
> +ARG is the numeric prefix argument that defaults to zero."
> +  (interactive "P")
> +  (kmacro-quit-counter '= arg))

Is there a reason why the code starts to use the raw prefix here
(capital "P")?


TIA,

Michael.





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

* bug#61549: 30.0.50; [PATCH] New keyboard macro counter functions
  2023-03-06  3:37       ` Alex Bochannek
  2023-03-11  8:49         ` Eli Zaretskii
  2023-03-12  0:19         ` Michael Heerdegen
@ 2024-05-22 23:57         ` Alex Bochannek
  2024-05-23  5:36           ` Eli Zaretskii
  2 siblings, 1 reply; 22+ messages in thread
From: Alex Bochannek @ 2024-05-22 23:57 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: larsi, monnier, 61549

I am picking up this patch again after not touching it for over a year.
I believe the last feedback was that the docstrings could use some
improvements.

Other than making sure the changes are still relevant and work, any
other recommendations for how to resubmit something that hasn't been
worked on for a while?

-- 
Alex.





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

* bug#61549: 30.0.50; [PATCH] New keyboard macro counter functions
  2024-05-22 23:57         ` Alex Bochannek
@ 2024-05-23  5:36           ` Eli Zaretskii
  2024-06-01  0:19             ` Alex Bochannek
  0 siblings, 1 reply; 22+ messages in thread
From: Eli Zaretskii @ 2024-05-23  5:36 UTC (permalink / raw)
  To: Alex Bochannek; +Cc: Michael Heerdegen, larsi, monnier, 61549

> From: Alex Bochannek <alex@bochannek.com>
> Cc: monnier@iro.umontreal.ca,  larsi@gnus.org,  61549@debbugs.gnu.org
> Date: Wed, 22 May 2024 16:57:51 -0700
> 
> I am picking up this patch again after not touching it for over a year.
> I believe the last feedback was that the docstrings could use some
> improvements.

No, not AFAICT.  There were review comments in

        https://debbugs.gnu.org/cgi/bugreport.cgi?bug=61549#20
and in
        https://debbugs.gnu.org/cgi/bugreport.cgi?bug=61549#23

and they were not only about documentation.

> Other than making sure the changes are still relevant and work, any
> other recommendations for how to resubmit something that hasn't been
> worked on for a while?

Rebase on the current master, that's all.

Thanks.





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

* bug#61549: 30.0.50; [PATCH] New keyboard macro counter functions
  2024-05-23  5:36           ` Eli Zaretskii
@ 2024-06-01  0:19             ` Alex Bochannek
  2024-06-02  6:01               ` Eli Zaretskii
  2024-06-02 23:33               ` Michael Heerdegen via Bug reports for GNU Emacs, the Swiss army knife of text editors
  0 siblings, 2 replies; 22+ messages in thread
From: Alex Bochannek @ 2024-06-01  0:19 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: Michael Heerdegen, larsi, monnier, 61549

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

Eli,

Thanks for the reminder of the other review comments. I went through
those and addressed them.

I am attaching the changes to:
  NEWS
  kmacro.el
  kmacro-tests.el

I hope this is the last iteration, but please let me know if there are
stylistic or implementation issues.

Thanks!

Eli Zaretskii <eliz@gnu.org> writes:

>> From: Alex Bochannek <alex@bochannek.com>
>> Cc: monnier@iro.umontreal.ca,  larsi@gnus.org,  61549@debbugs.gnu.org
>> Date: Wed, 22 May 2024 16:57:51 -0700
>> 
>> I am picking up this patch again after not touching it for over a year.
>> I believe the last feedback was that the docstrings could use some
>> improvements.
>
> No, not AFAICT.  There were review comments in
>
>         https://debbugs.gnu.org/cgi/bugreport.cgi?bug=61549#20
> and in
>         https://debbugs.gnu.org/cgi/bugreport.cgi?bug=61549#23
>
> and they were not only about documentation.

	Advanced keyboard macro counter commands for register
	integration and conditional macro termination

	* etc/NEWS:
	Document advanced keyboard macro counter commands.

	* lisp/kmacro.el (kmacro-keymap)
	(kmacro-reg-load-counter, kmacro-reg-save-counter)
	(kmacro-reg-add-counter-equal, kmacro-reg-add-counter-less)
	(kmacro-reg-add-counter-greater, kmacro-reg-add-counter)
	(kmacro-quit-counter-equal, kmacro-quit-counter-less)
	(kmacro-quit-counter-greater, kmacro-quit-counter):
	Add advanced keyboard macro counter commands to kmacro keymap.
	Implement advanced keyboard macro counter commands.

	* test/lisp/kmacro-tests.el (kmacro-tests-test-reg-load)
	(kmacro-tests-test-reg-save)
	(kmacro-tests-test-reg-add-counter-equal-01)
	(kmacro-tests-test-reg-add-counter-equal-02)
	(kmacro-tests-test-reg-add-counter-equal-03)
	(kmacro-tests-test-reg-add-counter-equal-04)
	(kmacro-tests-test-reg-add-counter-less)
	(kmacro-tests-test-reg-add-counter-greater)
	(kmacro-tests-test-quit-counter-equal-01)
	(kmacro-tests-test-quit-counter-equal-02)
	(kmacro-tests-test-quit-counter-equal-03)
	(kmacro-tests-test-quit-counter-equal-04)
	(kmacro-tests-test-quit-counter-less)
	(kmacro-tests-test-quit-counter-greater):
	Implement unit tests for advanced keyboard macro counter
	commands.

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: Type: text/x-patch, Size: 991 bytes --]

diff --git a/etc/NEWS b/etc/NEWS
index 3c672ffed8f..0675afe9505 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -1682,6 +1682,25 @@ The user option 'proced-auto-update-flag' can now be set to 2 additional
 values, which control automatic updates of Proced buffers that are not
 displayed in some window.
 
+** Kmacro
+
++++
+*** New Advanced Macro Counter functions.
+New commands have been added to implement advanced macro counter
+functions.
+
+The commands 'C-x C-k C-r l' and 'C-x C-k C-r s' load and save the
+macro counter from and to a number register, respectively.
+
+The commands 'C-x C-k C-r a =', 'C-x C-k C-r a <', and
+'C-x C-k C-r a >' compare the macro counter with the contents of a
+number register and increment the counter by an optional prefix if the
+comparison succeeds.
+
+The commands 'C-x C-k C-q =', 'C-x C-k C-q <', and 'C-x C-k C-q >'
+compare the macro counter with an optional prefix and terminate the
+macro if the comparison succeeds.
+
 ** Kmacro Menu mode
 
 +++

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: Type: text/x-patch, Size: 3885 bytes --]

diff --git a/lisp/kmacro.el b/lisp/kmacro.el
index 07a13d5632c..277de517443 100644
--- a/lisp/kmacro.el
+++ b/lisp/kmacro.el
@@ -187,6 +187,14 @@ kmacro-keymap
   "C-c"     #'kmacro-set-counter
   "C-i"     #'kmacro-insert-counter
   "C-a"     #'kmacro-add-counter
+  "C-r l"   #'kmacro-reg-load-counter
+  "C-r s"   #'kmacro-reg-save-counter
+  "C-r a =" #'kmacro-reg-add-counter-equal
+  "C-r a <" #'kmacro-reg-add-counter-less
+  "C-r a >" #'kmacro-reg-add-counter-greater
+  "C-q ="   #'kmacro-quit-counter-equal
+  "C-q <"   #'kmacro-quit-counter-less
+  "C-q >"   #'kmacro-quit-counter-greater
 
   ;; macro editing
   "C-e"  #'kmacro-edit-macro-repeat
@@ -346,6 +354,82 @@ kmacro-add-counter
   (unless executing-kbd-macro
     (kmacro-display-counter)))
 
+(defun kmacro-reg-load-counter (register)
+  "Load the value of a REGISTER into `kmacro-counter'."
+  (interactive
+   (list (register-read-with-preview "Load register to counter: ")))
+  (let ((register-val (get-register register)))
+    (when (numberp register-val)
+      (setq kmacro-counter register-val))))
+
+(defun kmacro-reg-save-counter (register)
+  "Save the value of `kmacro-counter' to a REGISTER."
+  (interactive
+   (list (register-read-with-preview "Save counter to register: ")))
+  (set-register register kmacro-counter))
+
+(defun kmacro-reg-add-counter-equal (&optional arg)
+  "Increment counter by one if it is equal to register value.
+Optional non-nil ARG specifies the increment."
+  (interactive "p")
+  (let
+      ((register (register-read-with-preview "Compare counter to register: ")))
+    (kmacro-reg-add-counter #'= register arg)))
+
+(defun kmacro-reg-add-counter-less (&optional arg)
+  "Increment counter by one if it is less than register value.
+Optional non-nil ARG specifies increment."
+  (interactive "p")
+  (let
+      ((register (register-read-with-preview "Compare counter to register: ")))
+    (kmacro-reg-add-counter #'< register arg)))
+
+
+(defun kmacro-reg-add-counter-greater (&optional arg)
+  "Increment counter by one if it is greater than register value.
+Optional non-nil ARG specifies increment."
+  (interactive "p")
+  (let
+      ((register (register-read-with-preview "Compare counter to register: ")))
+    (kmacro-reg-add-counter #'> register arg)))
+
+(defun kmacro-reg-add-counter (pred register arg)
+  "Increment `kmacro-counter' by ARG if PRED returns non-nil.
+PRED is called with two arguments: `kmacro-counter' and REGISTER."
+  (let ((register-val (get-register register)))
+    (when (funcall pred kmacro-counter register-val)
+      (setq current-prefix-arg nil)
+      (kmacro-add-counter arg))))
+
+(defun kmacro-quit-counter-equal (&optional arg)
+  "Quit the keyboard macro if the counter is equal to zero.
+Optional non-nil ARG specifies comparison value."
+  (interactive "p")
+  (kmacro-quit-counter #'= arg))
+
+(defun kmacro-quit-counter-less (&optional arg)
+  "Quit the keyboard macro if the counter is less than zero.
+Optional non-nil ARG specifies comparison value."
+  (interactive "p")
+  (kmacro-quit-counter #'< arg))
+
+(defun kmacro-quit-counter-greater (&optional arg)
+  "Quit the keyboard macro if the counter is greater than zero.
+Optional non-nil ARG specifies comparison value."
+  (interactive "p")
+  (kmacro-quit-counter #'> arg))
+
+(defun kmacro-quit-counter (pred arg)
+  "Quit the keyboard macro if PRED returns non-nil.
+PRED is called with two arguments: `kmacro-counter' and ARG.
+ARG specifies comparison value or zero if nil."
+  (when kmacro-initial-counter-value
+    (setq kmacro-counter kmacro-initial-counter-value
+          kmacro-initial-counter-value nil))
+  (let ((arg (if (null current-prefix-arg)
+                 0 arg)))
+    (when (funcall pred kmacro-counter arg)
+      (keyboard-quit))))
 
 (defun kmacro-loop-setup-function ()
   "Function called prior to each iteration of macro."

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #4: Type: text/x-patch, Size: 10891 bytes --]

diff --git a/test/lisp/kmacro-tests.el b/test/lisp/kmacro-tests.el
index 2eead234988..52abe138ae9 100644
--- a/test/lisp/kmacro-tests.el
+++ b/test/lisp/kmacro-tests.el
@@ -275,6 +275,219 @@ kmacro-tests-start-insert-counter-appends-to-macro
     ;;  Verify that the recording state has changed.
     (should (equal defining-kbd-macro 'append))))
 
+
+(kmacro-tests-deftest kmacro-tests-test-reg-load ()
+  "`kmacro-reg-load-counter' loads the value of a register into the counter."
+  (set-register ?\C-r 4) ;; Should be safe as a register name
+  (kmacro-tests-simulate-command '(kmacro-set-counter 1))
+  (kmacro-tests-define-macro (vconcat
+                              ;; Insert and increment counter
+                              "\C-x\C-k\C-i"
+                              "\C-x\C-k\C-i"
+                              ;; Load from register
+                              "\C-x\C-k\C-rl\C-r"
+                              ))
+  (kmacro-tests-should-insert "1245"
+    (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 2)))
+    (set-register ?\C-r nil))
+
+(kmacro-tests-deftest kmacro-tests-test-reg-save ()
+  "`kmacro-reg-save-counter' saves the counter to a register."
+  (set-register ?\C-r nil) ;; Should be safe as a register name
+  (kmacro-tests-simulate-command '(kmacro-set-counter 1))
+  (kmacro-tests-define-macro (vconcat
+                              ;; Insert and increment counter
+                              "\C-x\C-k\C-i"
+                              ;; Save to register
+                              "\C-x\C-k\C-rs\C-r"
+                              ;; Add to counter
+                              "\C-u2\C-x\C-k\C-a"
+                              ;; Insert and increment counter
+                              "\C-x\C-k\C-i"
+                              ;; Insert register
+                              "\C-xri\C-r"
+                              ))
+  (kmacro-tests-should-insert "142586"
+    (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 2)))
+  (set-register ?\C-r nil))
+
+
+(kmacro-tests-deftest kmacro-tests-test-reg-add-counter-equal-01 ()
+  "`kmacro-reg-add-counter-equal' increments counter by one if equal to register."
+  (set-register ?\C-r 2) ;; Should be safe as a register name
+  (kmacro-tests-define-macro (vconcat
+                              ;; Insert and increment counter
+                              "\C-x\C-k\C-i"
+                              ;; Increment counter if it matches
+                              "\C-x\C-k\C-ra=\C-r"
+                              ))
+  (kmacro-tests-should-insert "0134"
+    (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 4)))
+  (set-register ?\C-r nil))
+
+(kmacro-tests-deftest kmacro-tests-test-reg-add-counter-equal-02 ()
+  "`kmacro-reg-add-counter-equal' increments counter by prefix if equal to register."
+  (set-register ?\C-r 2) ;; Should be safe as a register name
+  (kmacro-tests-define-macro (vconcat
+                              ;; Insert and increment counter
+                              "\C-x\C-k\C-i"
+                              ;; Add two to counter if it matches
+                              "\C-u2\C-x\C-k\C-ra=\C-r"
+                              ))
+  (kmacro-tests-should-insert "0145"
+    (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 4)))
+  (set-register ?\C-r nil))
+
+(kmacro-tests-deftest kmacro-tests-test-reg-add-counter-equal-03 ()
+  "`kmacro-reg-add-counter-equal' increments counter by universal arg if equal to register."
+  (set-register ?\C-r 2) ;; Should be safe as a register name
+  (kmacro-tests-define-macro (vconcat
+                              ;; Insert and increment counter
+                              "\C-x\C-k\C-i"
+                              ;; Add four to counter if it matches
+                              "\C-u\C-x\C-k\C-ra=\C-r"
+                              ))
+  (kmacro-tests-should-insert "0167"
+    (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 4)))
+  (set-register ?\C-r nil))
+
+(kmacro-tests-deftest kmacro-tests-test-reg-add-counter-equal-04 ()
+  "`kmacro-reg-add-counter-equal' decrements counter by one if equal to register."
+  (set-register ?\C-r 2) ;; Should be safe as a register name
+  (kmacro-tests-define-macro (vconcat
+                              ;; Insert and increment counter
+                              "\C-x\C-k\C-i"
+                              ;; Decrement counter if it matches
+                              "\C-u-\C-x\C-k\C-ra=\C-r"
+                              ))
+  (kmacro-tests-should-insert "0111"
+    (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 4)))
+  (set-register ?\C-r nil))
+
+(kmacro-tests-deftest kmacro-tests-test-reg-add-counter-less ()
+  "`kmacro-reg-add-counter-less' decrements counter if less than register."
+  (set-register ?\C-r 6) ;; Should be safe as a register name
+  (kmacro-tests-simulate-command '(kmacro-set-counter 7))
+  (kmacro-tests-define-macro (vconcat
+                              ;; Decrement counter if it's
+                              ;; less than the register
+                              "\C-u-\C-x\C-k\C-ra<\C-r"
+                              ;; Insert and decrement counter
+                              "\C-u-\C-x\C-k\C-i"
+                              ))
+  (kmacro-tests-should-insert "7642"
+    (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 4)))
+    (set-register ?\C-r nil))
+
+(kmacro-tests-deftest kmacro-tests-test-reg-add-counter-greater ()
+  "`kmacro-reg-add-counter-greater' increments counter if greater than register."
+  (set-register ?\C-r 1) ;; Should be safe as a register name
+  (kmacro-tests-define-macro (vconcat
+                              ;; Insert and increment counter
+                              "\C-x\C-k\C-i"
+                              ;; Increment counter if it's greater
+                              ;; than the register
+                              "\C-x\C-k\C-ra>\C-r"
+                              ))
+  (kmacro-tests-should-insert "0135"
+    (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 4)))
+  (set-register ?\C-r nil))
+
+
+(kmacro-tests-deftest kmacro-tests-test-quit-counter-equal-01 ()
+  "`kmacro-quit-counter-equal' stops macro if counter is equal to positive prefix."
+  (kmacro-tests-define-macro (vconcat
+                              ;; Insert and increment counter
+                              "\C-x\C-k\C-i"
+                              ;; Stop if the counter is at 5
+                              "\C-u5\C-x\C-k\C-q="
+                              ))
+  (kmacro-tests-should-insert "0123"
+    (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 4)))
+  (should (= 4 kmacro-counter))
+  (should (condition-case abort
+            (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 1))
+            (quit abort))))
+
+(kmacro-tests-deftest kmacro-tests-test-quit-counter-equal-02 ()
+  "`kmacro-quit-counter-equal' stops macro if counter is equal to zero."
+  (kmacro-tests-simulate-command '(kmacro-set-counter 5))
+  (kmacro-tests-define-macro (vconcat
+                              ;; Insert and decrement counter
+                              "\C-u-\C-x\C-k\C-i"
+                              ;; Stop if the counter is at 0
+                              "\C-x\C-k\C-q="
+                              ))
+  (kmacro-tests-should-insert "5432"
+    (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 4)))
+  (should (= 1 kmacro-counter))
+  (should (condition-case abort
+              (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 1))
+            (quit abort))))
+
+(kmacro-tests-deftest kmacro-tests-test-quit-counter-equal-03 ()
+  "`kmacro-quit-counter-equal' stops macro if counter is equal to negative prefix."
+  (kmacro-tests-simulate-command '(kmacro-set-counter 4))
+  (kmacro-tests-define-macro (vconcat
+                              ;; Insert and decrement counter
+                              "\C-u-\C-x\C-k\C-i"
+                              ;; Stop if the counter is at -1
+                              "\C-u-\C-x\C-k\C-q="
+                              ))
+  (kmacro-tests-should-insert "4321"
+    (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 4)))
+  (should (= 0 kmacro-counter))
+  (should (condition-case abort
+              (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 1))
+            (quit abort))))
+
+(kmacro-tests-deftest kmacro-tests-test-quit-counter-equal-04 ()
+  "`kmacro-quit-counter-equal' doesn't stop macro if counter doesn't equal prefix."
+  (kmacro-tests-define-macro (vconcat
+                              ;; Insert and increment counter
+                              "\C-u2\C-x\C-k\C-i"
+                              ;; Stop if the counter is at 7
+                              "\C-u7\C-x\C-k\C-q="
+                              ))
+  (kmacro-tests-should-insert "0246"
+    (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 4)))
+  (should-not (condition-case abort
+              (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 1))
+              (quit abort)))
+  (should (= 10 kmacro-counter)))
+
+(kmacro-tests-deftest kmacro-tests-test-quit-counter-less ()
+  "`kmacro-quit-counter-less' stops macro if counter is less than prefix."
+  (kmacro-tests-simulate-command '(kmacro-set-counter 8))
+  (kmacro-tests-define-macro (vconcat
+                              ;; Stop if the counter is less than 5
+                              "\C-u5\C-x\C-k\C-q<"
+                              ;; Insert and decrement counter
+                              "\C-u-\C-x\C-k\C-i"
+                              ))
+  (kmacro-tests-should-insert "8765"
+    (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 4)))
+  (should (condition-case abort
+              (should (= 4 kmacro-counter))
+              (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 1))
+            (quit abort))))
+
+(kmacro-tests-deftest kmacro-tests-test-quit-counter-greater ()
+  "`kmacro-quit-counter-greater' stops macro if counter is greater than prefix."
+  (kmacro-tests-define-macro (vconcat
+                              ;; Insert and increment counter
+                              "\C-x\C-k\C-i"
+                              ;; Stop if the counter is greater than 4
+                              "\C-u4\C-x\C-k\C-q>"
+                              ))
+  (kmacro-tests-should-insert "0123"
+    (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 4)))
+  (should (condition-case abort
+              (should (= 4 kmacro-counter))
+              (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 1))
+            (quit abort))))
+
+
 (kmacro-tests-deftest kmacro-tests-end-call-macro-prefix-args ()
   "kmacro-end-call-macro changes behavior based on prefix arg."
   ;; "Record" two macros.

[-- Attachment #5: Type: text/plain, Size: 10 bytes --]

-- 
Alex.

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

* bug#61549: 30.0.50; [PATCH] New keyboard macro counter functions
  2024-06-01  0:19             ` Alex Bochannek
@ 2024-06-02  6:01               ` Eli Zaretskii
  2024-06-02 23:33               ` Michael Heerdegen via Bug reports for GNU Emacs, the Swiss army knife of text editors
  1 sibling, 0 replies; 22+ messages in thread
From: Eli Zaretskii @ 2024-06-02  6:01 UTC (permalink / raw)
  To: Alex Bochannek; +Cc: michael_heerdegen, larsi, monnier, 61549-done

> From: Alex Bochannek <alex@bochannek.com>
> Cc: monnier@iro.umontreal.ca,  larsi@gnus.org,  61549@debbugs.gnu.org,
>   Michael Heerdegen <michael_heerdegen@web.de>
> Date: Fri, 31 May 2024 17:19:02 -0700
> 
> Thanks for the reminder of the other review comments. I went through
> those and addressed them.
> 
> I am attaching the changes to:
>   NEWS
>   kmacro.el
>   kmacro-tests.el
> 
> I hope this is the last iteration, but please let me know if there are
> stylistic or implementation issues.

Thanks.  The patches are okay, but they didn't apply cleanly, so I
needed to apply by hand.  Please in the future send the patches in the
form of "git format-patch", which will make it easier to install.

I'm therefore closing this bug.  Thank you for working on this.





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

* bug#61549: 30.0.50; [PATCH] New keyboard macro counter functions
  2024-06-01  0:19             ` Alex Bochannek
  2024-06-02  6:01               ` Eli Zaretskii
@ 2024-06-02 23:33               ` Michael Heerdegen via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2024-06-03 11:33                 ` Eli Zaretskii
  1 sibling, 1 reply; 22+ messages in thread
From: Michael Heerdegen via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2024-06-02 23:33 UTC (permalink / raw)
  To: Alex Bochannek; +Cc: Eli Zaretskii, larsi, 61549, monnier

Alex Bochannek <alex@bochannek.com> writes:

> I hope this is the last iteration, but please let me know if there are
> stylistic or implementation issues.

Sorry, but some more small questions from my side (please excuse my
ignorance, I'm not a heavy user of keyboard macros so if any question
doesn't make sense maybe I'm just on a wrong track):

(1) "kmacro-add-counter" and related functions... I wonder why these are
not named "kmacro-increase-counter" - would that not be better
understandable?  I mean, AFAIU, you do not add a counter but add
something to an (or "the") counter.  But maybe I misunderstand.

(2) `kmacro-reg-load-counter': I think I would expect a `user-error'
being raised when the operation fails.  Unless that would get in the
way in your use case (break recording or so, dunno).

(3) I actually don't understand the usage of the
`kmacro-reg-add-counter-*' named functions (did you, Eli?).  An example
would maybe help.

In addition I want to say the suggested manual additions are useful.
Would it make sense to add at least some parts to the file header or
some pieces to the docstrings, or as comments?


Thanks,

Michael.





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

* bug#61549: 30.0.50; [PATCH] New keyboard macro counter functions
  2024-06-02 23:33               ` Michael Heerdegen via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2024-06-03 11:33                 ` Eli Zaretskii
  2024-06-04  0:56                   ` Michael Heerdegen via Bug reports for GNU Emacs, the Swiss army knife of text editors
  0 siblings, 1 reply; 22+ messages in thread
From: Eli Zaretskii @ 2024-06-03 11:33 UTC (permalink / raw)
  To: Michael Heerdegen; +Cc: 61549, larsi, monnier, alex

> From: Michael Heerdegen <michael_heerdegen@web.de>
> Cc: Eli Zaretskii <eliz@gnu.org>,  larsi@gnus.org,
>   monnier@iro.umontreal.ca,  61549@debbugs.gnu.org
> Date: Mon, 03 Jun 2024 01:33:54 +0200
> 
> Alex Bochannek <alex@bochannek.com> writes:
> 
> > I hope this is the last iteration, but please let me know if there are
> > stylistic or implementation issues.
> 
> Sorry, but some more small questions from my side (please excuse my
> ignorance, I'm not a heavy user of keyboard macros so if any question
> doesn't make sense maybe I'm just on a wrong track):

Thanks, but please in the future try to chime in earlier, while the
patches are still being discussed.  I intentionally wait for at least
a week before doing something, and in this case the thread was going
on for much longer.

> (1) "kmacro-add-counter" and related functions... I wonder why these are
> not named "kmacro-increase-counter" - would that not be better
> understandable?  I mean, AFAIU, you do not add a counter but add
> something to an (or "the") counter.  But maybe I misunderstand.

If we want to rename this, IMO kmacro-add-to-counter is a better name.

> (2) `kmacro-reg-load-counter': I think I would expect a `user-error'
> being raised when the operation fails.  Unless that would get in the
> way in your use case (break recording or so, dunno).

No opinion here, as I don't use those, either.

> (3) I actually don't understand the usage of the
> `kmacro-reg-add-counter-*' named functions (did you, Eli?).  An example
> would maybe help.

Did you read the doc string before or after I install the changes?

> In addition I want to say the suggested manual additions are useful.
> Would it make sense to add at least some parts to the file header or
> some pieces to the docstrings, or as comments?

No, I think these are sufficiently obscure to not be in the manual.





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

* bug#61549: 30.0.50; [PATCH] New keyboard macro counter functions
  2024-06-03 11:33                 ` Eli Zaretskii
@ 2024-06-04  0:56                   ` Michael Heerdegen via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2024-06-04  1:40                     ` Drew Adams via Bug reports for GNU Emacs, the Swiss army knife of text editors
                                       ` (2 more replies)
  0 siblings, 3 replies; 22+ messages in thread
From: Michael Heerdegen via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2024-06-04  0:56 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 61549, larsi, monnier, alex

Eli Zaretskii <eliz@gnu.org> writes:

> Thanks, but please in the future try to chime in earlier [...]

Sure - I'm sorry that I failed this time.

> > (1) "kmacro-add-counter" and related functions... I wonder why these are
> > not named "kmacro-increase-counter" [...]

> If we want to rename this, IMO kmacro-add-to-counter is a better name.

Sounds reasonable.

> > (3) I actually don't understand the usage of the
> > `kmacro-reg-add-counter-*' named functions (did you, Eli?).  An example
> > would maybe help.
>
> Did you read the doc string before or after I install the changes?

Both times in this case, actually.  My question was more about how this
is supposed to be used, not what it does.  Maybe it's obvious when one
uses kmacros often.

> > In addition I want to say the suggested manual additions are useful.
> > Would it make sense to add at least some parts to the file header or
> > some pieces to the docstrings, or as comments?
>
> No, I think these are sufficiently obscure to not be in the manual.

I agree - but the focus here was on the second sentence.


Thanks,

Michael.





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

* bug#61549: 30.0.50; [PATCH] New keyboard macro counter functions
  2024-06-04  0:56                   ` Michael Heerdegen via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2024-06-04  1:40                     ` Drew Adams via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2024-06-04  2:01                     ` Alex Bochannek
  2024-06-04 14:37                     ` Eli Zaretskii
  2 siblings, 0 replies; 22+ messages in thread
From: Drew Adams via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2024-06-04  1:40 UTC (permalink / raw)
  To: Michael Heerdegen, Eli Zaretskii
  Cc: alex@bochannek.com, larsi@gnus.org, monnier@iro.umontreal.ca,
	61549@debbugs.gnu.org

> > > not named "kmacro-increase-counter" [...]
> 
> > If we want to rename this, IMO kmacro-add-to-counter is a better name.
> 
> Sounds reasonable.

kmacro-increment-counter (if that's what
"adding to" it means here)






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

* bug#61549: 30.0.50; [PATCH] New keyboard macro counter functions
  2024-06-04  0:56                   ` Michael Heerdegen via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2024-06-04  1:40                     ` Drew Adams via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2024-06-04  2:01                     ` Alex Bochannek
  2024-06-05 13:52                       ` Michael Heerdegen via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2024-06-04 14:37                     ` Eli Zaretskii
  2 siblings, 1 reply; 22+ messages in thread
From: Alex Bochannek @ 2024-06-04  2:01 UTC (permalink / raw)
  To: Michael Heerdegen; +Cc: Eli Zaretskii, larsi, 61549, monnier

Michael, Eli,

Michael Heerdegen <michael_heerdegen@web.de> writes:

> Eli Zaretskii <eliz@gnu.org> writes:
>
>> Thanks, but please in the future try to chime in earlier [...]
>
> Sure - I'm sorry that I failed this time.
>
>> > (1) "kmacro-add-counter" and related functions... I wonder why these are
>> > not named "kmacro-increase-counter" [...]
>
>> If we want to rename this, IMO kmacro-add-to-counter is a better name.
>
> Sounds reasonable.

I tried to make these function names descriptive and if there is
something more obvious, I am happy to rename them. (Also see below)

>> > (3) I actually don't understand the usage of the
>> > `kmacro-reg-add-counter-*' named functions (did you, Eli?).  An example
>> > would maybe help.
>>
>> Did you read the doc string before or after I install the changes?
>
> Both times in this case, actually.  My question was more about how this
> is supposed to be used, not what it does.  Maybe it's obvious when one
> uses kmacros often.

I had some examples in the documentation in the original patch proposal.
The first set of functions lets you easily construct loops that change
the stepping of the counter based on a register value, so something
like:

C-x C-k TAB C-x C-k C-r a > T RET C-u 20 C-x C-k C-q >

When register T is set to 10 this will loop in steps of one to 10, then
in steps of two until it quits at 20.

I think the functions may be somewhat inconsistent because the first set
takes a prefix as an increment value and a register name as an argument
whereas the second uses the prefix as a the comparison value. A more
general approach would be to allow numeric registers for both types of
functions.

The problem I was trying to solve was how to use keyboard macros to
auto-create lists of numbers. I have found it useful and I can see how
other people might discover other uses. I figured it's been a over a
year since I originally proposed this, let's get this in and improve it
as there is some usage.

>> > In addition I want to say the suggested manual additions are useful.
>> > Would it make sense to add at least some parts to the file header or
>> > some pieces to the docstrings, or as comments?
>>
>> No, I think these are sufficiently obscure to not be in the manual.
>
> I agree - but the focus here was on the second sentence.

-- 
Alex.





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

* bug#61549: 30.0.50; [PATCH] New keyboard macro counter functions
  2024-06-04  0:56                   ` Michael Heerdegen via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2024-06-04  1:40                     ` Drew Adams via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2024-06-04  2:01                     ` Alex Bochannek
@ 2024-06-04 14:37                     ` Eli Zaretskii
  2 siblings, 0 replies; 22+ messages in thread
From: Eli Zaretskii @ 2024-06-04 14:37 UTC (permalink / raw)
  To: Michael Heerdegen; +Cc: 61549, larsi, monnier, alex

> From: Michael Heerdegen <michael_heerdegen@web.de>
> Cc: alex@bochannek.com,  larsi@gnus.org,  monnier@iro.umontreal.ca,
>   61549@debbugs.gnu.org
> Date: Tue, 04 Jun 2024 02:56:21 +0200
> 
> Eli Zaretskii <eliz@gnu.org> writes:
> 
> > > In addition I want to say the suggested manual additions are useful.
> > > Would it make sense to add at least some parts to the file header or
> > > some pieces to the docstrings, or as comments?
> >
> > No, I think these are sufficiently obscure to not be in the manual.
> 
> I agree - but the focus here was on the second sentence.

Feel free to suggest patches for documentation, and TIA.





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

* bug#61549: 30.0.50; [PATCH] New keyboard macro counter functions
  2024-06-04  2:01                     ` Alex Bochannek
@ 2024-06-05 13:52                       ` Michael Heerdegen via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2024-06-07 19:07                         ` Alex Bochannek
  2024-06-08  2:18                         ` Pranshu
  0 siblings, 2 replies; 22+ messages in thread
From: Michael Heerdegen via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2024-06-05 13:52 UTC (permalink / raw)
  To: Alex Bochannek; +Cc: Eli Zaretskii, larsi, 61549, monnier

Alex Bochannek <alex@bochannek.com> writes:

> The problem I was trying to solve was how to use keyboard macros to
> auto-create lists of numbers.

I see.

Does this cover the most frequent non-trivial use cases well?  AFAUI,
you allow to change the automatic increment in each macro step, but only
once - other cases would get complicated or are not possible.

Have you thought about the idea to provide some more explicit way to
control the automatic increment of a kmacro counter, like a mathematical
formula (a function of one argument, the old counter value, that would
calculate the next counter value)?  Given your experience with keyboard
macros usage, would that be a useful thing?


Thanks so far,

Michael.





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

* bug#61549: 30.0.50; [PATCH] New keyboard macro counter functions
  2024-06-05 13:52                       ` Michael Heerdegen via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2024-06-07 19:07                         ` Alex Bochannek
  2024-06-08 13:45                           ` Michael Heerdegen via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2024-06-08  2:18                         ` Pranshu
  1 sibling, 1 reply; 22+ messages in thread
From: Alex Bochannek @ 2024-06-07 19:07 UTC (permalink / raw)
  To: Michael Heerdegen; +Cc: Eli Zaretskii, larsi, 61549, monnier

Michael,

Michael Heerdegen <michael_heerdegen@web.de> writes:

> Have you thought about the idea to provide some more explicit way to
> control the automatic increment of a kmacro counter, like a mathematical
> formula (a function of one argument, the old counter value, that would
> calculate the next counter value)?  Given your experience with keyboard
> macros usage, would that be a useful thing?

I think this is a very reasonable idea. I'll have a look at how to
generalize this and submit a patch. It will probably take me a little
while, but it won't be a year again ;)

-- 
Alex.





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

* bug#61549: 30.0.50; [PATCH] New keyboard macro counter functions
  2024-06-05 13:52                       ` Michael Heerdegen via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2024-06-07 19:07                         ` Alex Bochannek
@ 2024-06-08  2:18                         ` Pranshu
  1 sibling, 0 replies; 22+ messages in thread
From: Pranshu @ 2024-06-08  2:18 UTC (permalink / raw)
  To: 61549; +Cc: alex, michael_heerdegen, monnier, eliz, larsi


Also there are multiple ways to include a seires of numbers, such as
'C-x r N'.  iirc calc has a way in which you can copy
numbers to a rectangleular region.  Apart form numbering lists, I am
unable to think of another use for using a formula for the macro counte.r





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

* bug#61549: 30.0.50; [PATCH] New keyboard macro counter functions
  2024-06-07 19:07                         ` Alex Bochannek
@ 2024-06-08 13:45                           ` Michael Heerdegen via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2024-06-11  3:06                             ` Alex Bochannek
  0 siblings, 1 reply; 22+ messages in thread
From: Michael Heerdegen via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2024-06-08 13:45 UTC (permalink / raw)
  To: Alex Bochannek; +Cc: Eli Zaretskii, larsi, 61549, monnier

Alex Bochannek <alex@bochannek.com> writes:

> I think this is a very reasonable idea. I'll have a look at how to
> generalize this and submit a patch.

Yes, this is the question.  AFAIK, there is currently no way to put an
Elisp function into a register.


Pranshu <pranshusharma366@gmail.com> writes:

> Also there are multiple ways to include a seires of numbers, such as
> 'C-x r N'.  iirc calc has a way in which you can copy numbers to a
> rectangleular region.  Apart form numbering lists, I am unable to
> think of another use for using a formula for the macro counte.r

@Alex: do you see other uses?


I think it would probably be easier to generalize C-x r N.  Should we
maybe just do that?


Thx,

Michael.





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

* bug#61549: 30.0.50; [PATCH] New keyboard macro counter functions
  2024-06-08 13:45                           ` Michael Heerdegen via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2024-06-11  3:06                             ` Alex Bochannek
  0 siblings, 0 replies; 22+ messages in thread
From: Alex Bochannek @ 2024-06-11  3:06 UTC (permalink / raw)
  To: Michael Heerdegen; +Cc: Eli Zaretskii, larsi, 61549, monnier

Michael,

Michael Heerdegen <michael_heerdegen@web.de> writes:

> Alex Bochannek <alex@bochannek.com> writes:
>
>> I think this is a very reasonable idea. I'll have a look at how to
>> generalize this and submit a patch.
>
> Yes, this is the question.  AFAIK, there is currently no way to put an
> Elisp function into a register.

This may not work, but I was thinking along the lines of storing the
text for a lambda in a string register and then doing something like:

(setq kmacro-counter (funcall (read (get-register ?A)) kmacro-counter))

Haven't really tried it and this may be a bad idea for any number of
reasons.

> Pranshu <pranshusharma366@gmail.com> writes:
>
>> Also there are multiple ways to include a seires of numbers, such as
>> 'C-x r N'.  iirc calc has a way in which you can copy numbers to a
>> rectangleular region.  Apart form numbering lists, I am unable to
>> think of another use for using a formula for the macro counte.r
>
> @Alex: do you see other uses?

I use keyboard macros frequently when I am manipulating multiple text
buffers to extract or merge data. Basically, anytime it gets slightly
too complicated for a quick AWK one-liner. The example of generating a
list of numbers came up in an IRC conversation and I wrote about it in a
blog post:

https://abochannek.github.io/emacs/2023/03/09/emacs-kbdmacros.html
(There is a link to a first part in that post that explains the history
of keyboard macros in Emacs.)

The examples are artificial and caused me to look into different ways to
do counter increments. After I implemented the conditional increment, I
found myself using it occasionally with keyboard macros to parse
irregular text files and collect bits and pieces from multiple buffers
that I then numbered in different ways. I certainly don't use it
frequently and the conditional quit is more useful.

The way I used to do construct lists with macros before is to have a
buffer with pre-generated numbers and have the macro kill/yank numbers
from there. Generalizing C-x r N may be helpful for that, I tend to just
use C-u M-! and then seq or jot, depending on what I need.

You may notice a couple of things in my blog post. I am suggesting
number registers as an alternative to the macro counter. They work well
in most cases except that they don't have a way to change the increment
conditionally either. Secondly, I am jealously looking at Calc macros,
because what I really want for keyboard macros is programmability with
proper loops and conditional branching.

I don't honestly know (nor do I know how to find out) how widely
keyboard macros in Emacs are used. I quickly record a new macro almost
daily and have in the past stored more complicated named macros in my
init file for later recall. Because what I use them for is usually
one-time work, I have not built up a library of functions for myself. I
would guess that my average keyboard macro is maybe between five and
twenty commands.

-- 
Alex.





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

end of thread, other threads:[~2024-06-11  3:06 UTC | newest]

Thread overview: 22+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-02-16  8:17 bug#61549: 30.0.50; [PATCH] New keyboard macro counter functions Alex Bochannek
2023-02-17  8:13 ` Eli Zaretskii
2023-02-19  1:59   ` Alex Bochannek
2023-02-19  6:54     ` Eli Zaretskii
2023-03-06  3:37       ` Alex Bochannek
2023-03-11  8:49         ` Eli Zaretskii
2023-03-12  0:19         ` Michael Heerdegen
2024-05-22 23:57         ` Alex Bochannek
2024-05-23  5:36           ` Eli Zaretskii
2024-06-01  0:19             ` Alex Bochannek
2024-06-02  6:01               ` Eli Zaretskii
2024-06-02 23:33               ` Michael Heerdegen via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-06-03 11:33                 ` Eli Zaretskii
2024-06-04  0:56                   ` Michael Heerdegen via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-06-04  1:40                     ` Drew Adams via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-06-04  2:01                     ` Alex Bochannek
2024-06-05 13:52                       ` Michael Heerdegen via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-06-07 19:07                         ` Alex Bochannek
2024-06-08 13:45                           ` Michael Heerdegen via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-06-11  3:06                             ` Alex Bochannek
2024-06-08  2:18                         ` Pranshu
2024-06-04 14:37                     ` 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).