emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

master 5281946fbf 1/3: Make format-spec accept function substitutions


From: Stefan Kangas
Subject: master 5281946fbf 1/3: Make format-spec accept function substitutions
Date: Thu, 29 Sep 2022 10:10:39 -0400 (EDT)

branch: master
commit 5281946fbf6b3cdbec5ce82e0057c71849faf4d2
Author: Stefan Kangas <stefankangas@gmail.com>
Commit: Stefan Kangas <stefankangas@gmail.com>

    Make format-spec accept function substitutions
    
    * 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       |  5 +++++
 etc/NEWS                       |  6 ++++++
 lisp/format-spec.el            | 17 +++++++++++++++--
 test/lisp/format-spec-tests.el | 11 +++++++++++
 4 files changed, 37 insertions(+), 2 deletions(-)

diff --git a/doc/lispref/strings.texi b/doc/lispref/strings.texi
index 374381e595..ba247a3eda 100644
--- a/doc/lispref/strings.texi
+++ b/doc/lispref/strings.texi
@@ -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
diff --git a/etc/NEWS b/etc/NEWS
index 4bab95da51..2f96072bfb 100644
--- 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
diff --git a/lisp/format-spec.el b/lisp/format-spec.el
index 45c19aebc8..60ff9f9086 100644
--- a/lisp/format-spec.el
+++ b/lisp/format-spec.el
@@ -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)))))
diff --git a/test/lisp/format-spec-tests.el b/test/lisp/format-spec-tests.el
index 4a3cc74c33..bd493ae1d7 100644
--- a/test/lisp/format-spec-tests.el
+++ b/test/lisp/format-spec-tests.el
@@ -148,6 +148,17 @@
              (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"))))



reply via email to

[Prev in Thread] Current Thread [Next in Thread]