summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars Ingebrigtsen <larsi@gnus.org>2020-12-29 03:04:51 +0100
committerLars Ingebrigtsen <larsi@gnus.org>2020-12-29 03:04:51 +0100
commit40d16332597d3aa564c9950ae1831faf6867c71a (patch)
treeef7070e55d22a6348beb33594323ca68ae20f36f
parent3334dd904157e7b3787f5d32f30b3c31585d047c (diff)
downloademacs-40d16332597d3aa564c9950ae1831faf6867c71a.tar.gz
Add a SPLIT parameter to `format-spec'
* doc/lispref/strings.texi (Custom Format Strings): Document it. * lisp/format-spec.el (format-spec): Add an optional parameter to return a list of strings (bug#33740).
-rw-r--r--doc/lispref/strings.texi12
-rw-r--r--etc/NEWS5
-rw-r--r--lisp/format-spec.el103
-rw-r--r--test/lisp/format-spec-tests.el10
4 files changed, 85 insertions, 45 deletions
diff --git a/doc/lispref/strings.texi b/doc/lispref/strings.texi
index a31e71d5260..4ac5057454f 100644
--- a/doc/lispref/strings.texi
+++ b/doc/lispref/strings.texi
@@ -1216,7 +1216,7 @@ The function @code{format-spec} described in this section performs a
similar function to @code{format}, except it operates on format
control strings that use arbitrary specification characters.
-@defun format-spec template spec-alist &optional ignore-missing
+@defun format-spec template spec-alist &optional ignore-missing split
This function returns a string produced from the format string
@var{template} according to conversions specified in @var{spec-alist},
which is an alist (@pxref{Association Lists}) of the form
@@ -1258,6 +1258,16 @@ any; if it is @code{delete}, those format specifications are removed
from the output; any other non-@code{nil} value is handled like
@code{ignore}, but any occurrences of @samp{%%} are also left verbatim
in the output.
+
+If the optional argument @var{split} is non-@code{nil}, instead of
+returning a single string, @code{format-spec} will split the result
+into a list of strings, based on where the substitutions were
+performed. For instance:
+
+@example
+(format-spec "foo %b bar" '((?b . "zot")) nil t)
+ @result{} ("foo " "zot" " bar")
+@end example
@end defun
The syntax of format specifications accepted by @code{format-spec} is
diff --git a/etc/NEWS b/etc/NEWS
index f8282696e46..cd4006170d2 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -2179,6 +2179,11 @@ In order for the two functions to behave more consistently,
length, and also supports format specifications that include a
truncating precision field, such as "%.2a".
++++
+** 'format-spec' now takes an optional SPLIT parameter.
+If non-nil, 'format-spec' will split the resulting string into a list
+of strings, based on where the format specs (and expansions) were.
+
---
** New function 'color-values-from-color-spec'.
This can be used to parse RGB color specs in several formats and
diff --git a/lisp/format-spec.el b/lisp/format-spec.el
index 6af79a44167..3abcd5183a3 100644
--- a/lisp/format-spec.el
+++ b/lisp/format-spec.el
@@ -25,7 +25,7 @@
;;; Code:
;;;###autoload
-(defun format-spec (format specification &optional ignore-missing)
+(defun format-spec (format specification &optional ignore-missing split)
"Return a string based on FORMAT and SPECIFICATION.
FORMAT is a string containing `format'-like specs like \"su - %u %k\".
SPECIFICATION is an alist mapping format specification characters
@@ -68,50 +68,65 @@ error; if it is the symbol `ignore', leave those %-specs verbatim
in the result, including their text properties, if any; if it is
the symbol `delete', remove those %-specs from the result;
otherwise do the same as for the symbol `ignore', but also leave
-any occurrences of \"%%\" in FORMAT verbatim in the result."
+any occurrences of \"%%\" in FORMAT verbatim in the result.
+
+If SPLIT, instead of returning a single string, a list of strings
+is returned, where each format spec is its own element."
(with-temp-buffer
- (insert format)
- (goto-char (point-min))
- (while (search-forward "%" nil t)
- (cond
- ;; Quoted percent sign.
- ((= (following-char) ?%)
- (when (memq ignore-missing '(nil ignore delete))
- (delete-char 1)))
- ;; Valid format spec.
- ((looking-at (rx (? (group (+ (in " 0<>^_-"))))
- (? (group (+ digit)))
- (? (group ?. (+ digit)))
- (group alpha)))
- (let* ((beg (point))
- (end (match-end 0))
- (flags (match-string 1))
- (width (match-string 2))
- (trunc (match-string 3))
- (char (string-to-char (match-string 4)))
- (text (assq char specification)))
- (cond (text
- ;; Handle flags.
- (setq text (format-spec--do-flags
- (format "%s" (cdr text))
- (format-spec--parse-flags flags)
- (and width (string-to-number width))
- (and trunc (car (read-from-string trunc 1)))))
- ;; Insert first, to preserve text properties.
- (insert-and-inherit text)
- ;; Delete the specifier body.
- (delete-region (point) (+ end (length text)))
- ;; Delete the percent sign.
- (delete-region (1- beg) beg))
- ((eq ignore-missing 'delete)
- ;; Delete the whole format spec.
- (delete-region (1- beg) end))
- ((not ignore-missing)
- (error "Invalid format character: `%%%c'" char)))))
- ;; Signal an error on bogus format strings.
- ((not ignore-missing)
- (error "Invalid format string"))))
- (buffer-string)))
+ (let ((split-start (point-min))
+ (split-result nil))
+ (insert format)
+ (goto-char (point-min))
+ (while (search-forward "%" nil t)
+ (cond
+ ;; Quoted percent sign.
+ ((= (following-char) ?%)
+ (when (memq ignore-missing '(nil ignore delete))
+ (delete-char 1)))
+ ;; Valid format spec.
+ ((looking-at (rx (? (group (+ (in " 0<>^_-"))))
+ (? (group (+ digit)))
+ (? (group ?. (+ digit)))
+ (group alpha)))
+ (let* ((beg (point))
+ (end (match-end 0))
+ (flags (match-string 1))
+ (width (match-string 2))
+ (trunc (match-string 3))
+ (char (string-to-char (match-string 4)))
+ (text (assq char specification)))
+ (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-spec--parse-flags flags)
+ (and width (string-to-number width))
+ (and trunc (car (read-from-string trunc 1)))))
+ ;; Insert first, to preserve text properties.
+ (insert-and-inherit text)
+ ;; Delete the specifier body.
+ (delete-region (point) (+ end (length text)))
+ ;; Delete the percent sign.
+ (delete-region (1- beg) beg))
+ ((eq ignore-missing 'delete)
+ ;; Delete the whole format spec.
+ (delete-region (1- beg) end))
+ ((not ignore-missing)
+ (error "Invalid format character: `%%%c'" char)))
+ (when split
+ (push (buffer-substring (1- beg) (point)) split-result)
+ (setq split-start (point)))))
+ ;; Signal an error on bogus format strings.
+ ((not ignore-missing)
+ (error "Invalid format string"))))
+ (if (not split)
+ (buffer-string)
+ (unless (= split-start (point-max))
+ (push (buffer-substring split-start (point-max)) split-result))
+ (nreverse split-result)))))
(defun format-spec--do-flags (str flags width trunc)
"Return STR formatted according to FLAGS, WIDTH, and TRUNC.
diff --git a/test/lisp/format-spec-tests.el b/test/lisp/format-spec-tests.el
index 11882217afb..cced8623330 100644
--- a/test/lisp/format-spec-tests.el
+++ b/test/lisp/format-spec-tests.el
@@ -178,4 +178,14 @@
(should (equal (format-spec "foo %>4b zot" '((?b . "longbar")))
"foo long zot")))
+(ert-deftest format-spec-split ()
+ (should (equal (format-spec "foo %b bar" '((?b . "zot")) nil t)
+ '("foo " "zot" " bar")))
+ (should (equal (format-spec "%b bar" '((?b . "zot")) nil t)
+ '("zot" " bar")))
+ (should (equal (format-spec "%b" '((?b . "zot")) nil t)
+ '("zot")))
+ (should (equal (format-spec "foo %b" '((?b . "zot")) nil t)
+ '("foo " "zot"))))
+
;;; format-spec-tests.el ends here