1
0
mirror of https://git.savannah.gnu.org/git/emacs.git synced 2024-12-13 09:32:47 +00:00

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).
This commit is contained in:
Lars Ingebrigtsen 2020-12-29 03:04:51 +01:00
parent 3334dd9041
commit 40d1633259
4 changed files with 85 additions and 45 deletions

View File

@ -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

View File

@ -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

View File

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

View File

@ -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