]> git.eshelyaron.com Git - emacs.git/commitdiff
Make format-spec accept function substitutions
authorStefan Kangas <stefankangas@gmail.com>
Tue, 27 Sep 2022 16:16:51 +0000 (18:16 +0200)
committerStefan Kangas <stefankangas@gmail.com>
Thu, 29 Sep 2022 13:39:27 +0000 (15:39 +0200)
* lisp/format-spec.el (format-spec): Accept a function producing the
substitution for a character.
* doc/lispref/strings.texi (Custom Format Strings): Document the
above change.
* test/lisp/format-spec-tests.el (format-spec/function): New test.
Ref. https://lists.gnu.org/r/emacs-devel/2022-09/msg01875.html

doc/lispref/strings.texi
etc/NEWS
lisp/format-spec.el
test/lisp/format-spec-tests.el

index 374381e595531face861ddafce14a43d9c0b3379..ba247a3edaee8958d5cbc553e2ae167649959dfa 100644 (file)
@@ -1293,6 +1293,11 @@ The order of specifications in @var{template} need not correspond to
 the order of associations in @var{spec-alist}.
 @end itemize
 
+REPLACEMENT can also be a function taking no arguments, and returning
+a string to be used for the replacement.  It will only be called when
+the corresponding LETTER is used in the TEMPLATE.  This is useful, for
+example, to avoid prompting for input unless it is needed.
+
 The optional argument @var{ignore-missing} indicates how to handle
 specification characters in @var{template} that are not found in
 @var{spec-alist}.  If it is @code{nil} or omitted, the function
index 4bab95da5122fd5492edf7fdf1ba4183b7ec4c26..2f96072bfb0a1e21521cd1f6d7806e3235885550 100644 (file)
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -3926,6 +3926,12 @@ the same but works by modifying LIST destructively.
 ---
 ** 'string-split' is now an alias for 'split-string'.
 
++++
+** 'format-spec' now accepts functions in the replacement.
+The function is called only when used in the format string.  This is
+useful to avoid side-effects such as prompting, when the value is not
+actually being used for anything.
+
 +++
 ** The variable 'max-specpdl-size' has been made obsolete.
 Now 'max-lisp-eval-depth' alone is used for limiting Lisp recursion
index 45c19aebc8bc45a72810380f5d4ba75d0b6ef74e..60ff9f90864cc6e83de626e0590b2a2d66046ba4 100644 (file)
@@ -59,6 +59,18 @@ value associated with ?b in SPECIFICATION, either padding it with
 leading zeros or truncating leading characters until it's ten
 characters wide\".
 
+the substitution for a specification character can also be a
+function, taking no arguments and returning a string to be used
+for the replacement.  It will only be called if FORMAT uses that
+character.  For example:
+
+  (format-spec \"%n\"
+               \\=`((?n . ,(lambda ()
+                          (read-number \"Number: \")))))
+
+Note that it is best to make sure the function is not quoted,
+like above, so that it is compiled by the byte-compiler.
+
 Any text properties of FORMAT are copied to the result, with any
 text properties of a %-spec itself copied to its substitution.
 
@@ -94,14 +106,15 @@ is returned, where each format spec is its own element."
                  (width (match-string 2))
                  (trunc (match-string 3))
                  (char (string-to-char (match-string 4)))
-                 (text (assq char specification)))
+                 (text (let ((res (cdr (assq char specification))))
+                         (if (functionp res) (funcall res) res))))
             (when (and split
                        (not (= (1- beg) split-start)))
               (push (buffer-substring split-start (1- beg)) split-result))
             (cond (text
                    ;; Handle flags.
                    (setq text (format-spec--do-flags
-                               (format "%s" (cdr text))
+                               (format "%s" text)
                                (format-spec--parse-flags flags)
                                (and width (string-to-number width))
                                (and trunc (car (read-from-string trunc 1)))))
index 4a3cc74c3343b4eb71ad4b9b1913996df6492855..bd493ae1d714b719fad5253b46899a0c0792cca2 100644 (file)
              (format-spec fmt '((?b . "asd") (?a . "fgh")))
              #("fgh%asdasd" 0 3 (a b) 3 4 (c d) 7 10 (e f))))))
 
+(ert-deftest format-spec/function ()
+  (let* (called
+         (spec `((?a . "foo")
+                 (?f . ,(lambda ()
+                          (setq called t)
+                          "bar")))))
+    (should (equal (format-spec "%a" spec) "foo"))
+    (should-not called)
+    (should (equal (format-spec "%f" spec) "bar"))
+    (should called)))
+
 (ert-deftest format-spec-unknown ()
   (should-error (format-spec "foo %b %z zot" '((?b . "bar"))))
   (should-error (format-spec "foo %b %%%z zot" '((?b . "bar"))))