Fix and extend format-spec (bug#41758)

* lisp/format-spec.el: Use lexical-binding.  Remove dependence on
subr-x.el.
(format-spec-make): Clarify docstring.
(format-spec--parse-modifiers): Rename to...
(format-spec--parse-flags): ...this and simplify.  In particular,
don't bother parsing :space-pad which is redundant and unused.
(format-spec--pad): Remove, replacing with...
(format-spec--do-flags): ...this new helper function which performs
more of format-spec's supported text manipulation.
(format-spec): Autoload.  Allow optional argument to take on special
values 'ignore' and 'delete' for more control over what happens when
a replacement for a format specification isn't provided.  Bring back
proper support for a precision modifier similar to that of 'format'.

* lisp/battery.el (battery-format): Rewrite in terms of format-spec.
(battery-echo-area-format, battery-mode-line-format): Mention
support of format-spec syntax in docstrings.

* doc/lispref/strings.texi (Custom Format Strings):
* etc/NEWS: Document and announce these changes.

* lisp/dired-aux.el (dired-do-compress-to):
* lisp/erc/erc-match.el (erc-log-matches):
* lisp/erc/erc.el (erc-update-mode-line-buffer):
* lisp/gnus/gnus-sieve.el (gnus-sieve-update):
* lisp/gnus/gssapi.el (open-gssapi-stream):
* lisp/gnus/mail-source.el (mail-source-fetch-file)
(mail-source-fetch-directory, mail-source-fetch-pop)
(mail-source-fetch-imap):
* lisp/gnus/message.el (message-insert-formatted-citation-line):
* lisp/image-dired.el:
* lisp/net/eww.el:
* lisp/net/imap.el (imap-kerberos4-open, imap-gssapi-open)
(imap-shell-open):
* lisp/net/network-stream.el (network-stream-open-shell):
* lisp/obsolete/tls.el (open-tls-stream):
* lisp/textmodes/tex-mode.el:
Remove extraneous loads and autoloads of format-spec now that it is
autoloaded and simplify its uses where possible.

* test/lisp/battery-tests.el (battery-format): Test new format-spec
support.
* test/lisp/format-spec-tests.el (test-format-spec): Rename to...
(format-spec) ...this, extending test cases.
(test-format-unknown): Rename to...
(format-spec-unknown): ...this, extending test cases.
(test-format-modifiers): Rename to...
(format-spec-flags): ...this.
(format-spec-make, format-spec-parse-flags, format-spec-do-flags)
(format-spec-do-flags-truncate, format-spec-do-flags-pad)
(format-spec-do-flags-chop, format-spec-do-flags-case): New tests.
This commit is contained in:
Basil L. Contovounesios 2020-05-29 19:56:14 +01:00
parent 97d1f672ac
commit 0185d76e74
19 changed files with 407 additions and 290 deletions

View file

@ -1152,7 +1152,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 only-present
@defun format-spec template spec-alist &optional ignore-missing
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
@ -1185,12 +1185,15 @@ The order of specifications in @var{template} need not correspond to
the order of associations in @var{spec-alist}.
@end itemize
The optional argument @var{only-present} indicates how to handle
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
signals an error. Otherwise, those format specifications and any
occurrences of @samp{%%} in @var{template} are left verbatim in the
output, including their text properties, if any.
signals an error; if it is @code{ignore}, those format specifications
are left verbatim in the output, including their text properties, if
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.
@end defun
The syntax of format specifications accepted by @code{format-spec} is
@ -1238,7 +1241,7 @@ the right rather than the left.
@item <
This flag causes the substitution to be truncated on the left to the
given width, if specified.
given width and precision, if specified.
@item >
This flag causes the substitution to be truncated on the right to the
@ -1257,9 +1260,12 @@ The result of using contradictory flags (for instance, both upper and
lower case) is undefined.
As is the case with @code{format}, a format specification can include
a width, which is a decimal number that appears after any flags. If a
substitution contains fewer characters than its specified width, it is
padded on the left:
a width, which is a decimal number that appears after any flags, and a
precision, which is a decimal-point @samp{.} followed by a decimal
number that appears after any flags and width.
If a substitution contains fewer characters than its specified width,
it is padded on the left:
@example
@group
@ -1269,6 +1275,17 @@ padded on the left:
@end group
@end example
If a substitution contains more characters than its specified
precision, it is truncated on the right:
@example
@group
(format-spec "%.2a is truncated on the right"
'((?a . "alpha")))
@result{} "al is truncated on the right"
@end group
@end example
Here is a more complicated example that combines several
aforementioned features:

View file

@ -461,6 +461,16 @@ In Emacs 24.3, the variable 'dbus-event-error-hooks' was renamed to
The old names, which were kept as obsolete aliases of the new names,
have now been removed.
** Battery
---
*** A richer syntax can be used to format battery status information.
The user options 'battery-mode-line-format' and
'battery-echo-area-format' now support the full formatting syntax of
the function 'format-spec' documented under '(elisp) Custom Format
Strings'. The new syntax includes specifiers for padding and
truncation, amongst other things.
* New Modes and Packages in Emacs 28.1
@ -578,6 +588,13 @@ for encoding and decoding without having to bind
It controls, whether 'process-file' returns a string when a remote
process is interrupted by a signal.
+++
** The behavior of 'format-spec' is now closer to that of 'format'.
In order for the two functions to behave more consistently,
'format-spec' now pads and truncates based on string width rather than
length, and also supports format specifications that include a
truncating precision field, such as '%.2a'.
* Changes in Emacs 28.1 on Non-Free Operating Systems

View file

@ -121,7 +121,10 @@ string are substituted as defined by the current value of the variable
%p Battery load percentage
%m Remaining time (to charge or discharge) in minutes
%h Remaining time (to charge or discharge) in hours
%t Remaining time (to charge or discharge) in the form `h:min'"
%t Remaining time (to charge or discharge) in the form `h:min'
The full `format-spec' formatting syntax is supported."
:link '(info-link "(elisp) Custom Format Strings")
:type '(choice string (const nil)))
(defvar battery-mode-line-string nil
@ -153,7 +156,10 @@ string are substituted as defined by the current value of the variable
%p Battery load percentage
%m Remaining time (to charge or discharge) in minutes
%h Remaining time (to charge or discharge) in hours
%t Remaining time (to charge or discharge) in the form `h:min'"
%t Remaining time (to charge or discharge) in the form `h:min'
The full `format-spec' formatting syntax is supported."
:link '(info-link "(elisp) Custom Format Strings")
:type '(choice string (const nil)))
(defcustom battery-update-interval 60
@ -823,13 +829,7 @@ The following %-sequences are provided:
(defun battery-format (format alist)
"Substitute %-sequences in FORMAT."
(replace-regexp-in-string
"%."
(lambda (str)
(let ((char (aref str 1)))
(if (eq char ?%) "%"
(or (cdr (assoc char alist)) ""))))
format t t))
(format-spec format alist 'delete))
(defun battery-search-for-one-match-in-files (files regexp match-num)
"Search REGEXP in the content of the files listed in FILES.

View file

@ -1064,8 +1064,6 @@ corresponding command.
Within CMD, %i denotes the input file(s), and %o denotes the
output file. %i path(s) are relative, while %o is absolute.")
(declare-function format-spec "format-spec.el" (format specification))
;;;###autoload
(defun dired-do-compress-to ()
"Compress selected files and directories to an archive.
@ -1073,7 +1071,6 @@ Prompt for the archive file name.
Choose the archiving command based on the archive file-name extension
and `dired-compress-files-alist'."
(interactive)
(require 'format-spec)
(let* ((in-files (dired-get-marked-files nil nil nil nil t))
(out-file (expand-file-name (read-file-name "Compress to: ")))
(rule (cl-find-if
@ -1093,12 +1090,12 @@ and `dired-compress-files-alist'."
(when (zerop
(dired-shell-command
(format-spec (cdr rule)
`((?\o . ,(shell-quote-argument out-file))
(?\i . ,(mapconcat
(lambda (file-desc)
(shell-quote-argument (file-name-nondirectory
file-desc)))
in-files " "))))))
`((?o . ,(shell-quote-argument out-file))
(?i . ,(mapconcat
(lambda (in-file)
(shell-quote-argument
(file-name-nondirectory in-file)))
in-files " "))))))
(message (ngettext "Compressed %d file to %s"
"Compressed %d files to %s"
(length in-files))

View file

@ -555,16 +555,15 @@ See `erc-log-match-format'."
(and (eq erc-log-matches-flag 'away)
(erc-away-time)))
match-buffer-name)
(let ((line (format-spec erc-log-match-format
(format-spec-make
?n nick
?t (format-time-string
(or (and (boundp 'erc-timestamp-format)
erc-timestamp-format)
"[%Y-%m-%d %H:%M] "))
?c (or (erc-default-target) "")
?m message
?u nickuserhost))))
(let ((line (format-spec
erc-log-match-format
`((?n . ,nick)
(?t . ,(format-time-string
(or (bound-and-true-p erc-timestamp-format)
"[%Y-%m-%d %H:%M] ")))
(?c . ,(or (erc-default-target) ""))
(?m . ,message)
(?u . ,nickuserhost)))))
(with-current-buffer (erc-log-matches-make-buffer match-buffer-name)
(let ((inhibit-read-only t))
(goto-char (point-max))

View file

@ -6391,17 +6391,16 @@ if `erc-away' is non-nil."
(defun erc-update-mode-line-buffer (buffer)
"Update the mode line in a single ERC buffer BUFFER."
(with-current-buffer buffer
(let ((spec (format-spec-make
?a (erc-format-away-status)
?l (erc-format-lag-time)
?m (erc-format-channel-modes)
?n (or (erc-current-nick) "")
?N (erc-format-network)
?o (or (erc-controls-strip erc-channel-topic) "")
?p (erc-port-to-string erc-session-port)
?s (erc-format-target-and/or-server)
?S (erc-format-target-and/or-network)
?t (erc-format-target)))
(let ((spec `((?a . ,(erc-format-away-status))
(?l . ,(erc-format-lag-time))
(?m . ,(erc-format-channel-modes))
(?n . ,(or (erc-current-nick) ""))
(?N . ,(erc-format-network))
(?o . ,(or (erc-controls-strip erc-channel-topic) ""))
(?p . ,(erc-port-to-string erc-session-port))
(?s . ,(erc-format-target-and/or-server))
(?S . ,(erc-format-target-and/or-network))
(?t . ,(erc-format-target))))
(process-status (cond ((and (erc-server-process-alive)
(not erc-server-connected))
":connecting")

View file

@ -1,4 +1,4 @@
;;; format-spec.el --- functions for formatting arbitrary formatting strings
;;; format-spec.el --- format arbitrary formatting strings -*- lexical-binding: t -*-
;; Copyright (C) 1999-2020 Free Software Foundation, Inc.
@ -24,10 +24,8 @@
;;; Code:
(eval-when-compile
(require 'subr-x))
(defun format-spec (format specification &optional only-present)
;;;###autoload
(defun format-spec (format specification &optional ignore-missing)
"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
@ -39,22 +37,22 @@ For instance:
\\=`((?u . ,(user-login-name))
(?l . \"ls\")))
Each %-spec may contain optional flag and width modifiers, as
follows:
Each %-spec may contain optional flag, width, and precision
modifiers, as follows:
%<flags><width>character
%<flags><width><precision>character
The following flags are allowed:
* 0: Pad to the width, if given, with zeros instead of spaces.
* -: Pad to the width, if given, on the right instead of the left.
* <: Truncate to the width, if given, on the left.
* >: Truncate to the width, if given, on the right.
* <: Truncate to the width and precision, if given, on the left.
* >: Truncate to the width and precision, if given, on the right.
* ^: Convert to upper case.
* _: Convert to lower case.
The width modifier behaves like the corresponding one in `format'
when applied to %s.
The width and truncation modifiers behave like the corresponding
ones in `format' when applied to %s.
For example, \"%<010b\" means \"substitute into the output the
value associated with ?b in SPECIFICATION, either padding it with
@ -64,89 +62,108 @@ characters wide\".
Any text properties of FORMAT are copied to the result, with any
text properties of a %-spec itself copied to its substitution.
ONLY-PRESENT indicates how to handle %-spec characters not
IGNORE-MISSING indicates how to handle %-spec characters not
present in SPECIFICATION. If it is nil or omitted, emit an
error; otherwise leave those %-specs and any occurrences of
\"%%\" in FORMAT verbatim in the result, including their text
properties, if any."
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."
(with-temp-buffer
(insert format)
(goto-char (point-min))
(while (search-forward "%" nil t)
(cond
;; Quoted percent sign.
((eq (char-after) ?%)
(unless only-present
(delete-char 1)))
;; Valid format spec.
((looking-at "\\([-0 _^<>]*\\)\\([0-9.]*\\)\\([a-zA-Z]\\)")
(let* ((modifiers (match-string 1))
(num (match-string 2))
(spec (string-to-char (match-string 3)))
(val (assq spec specification)))
(if (not val)
(unless only-present
(error "Invalid format character: `%%%c'" spec))
(setq val (cdr val)
modifiers (format-spec--parse-modifiers modifiers))
;; Pad result to desired length.
(let ((text (format "%s" val)))
(when num
(setq num (string-to-number num))
(setq text (format-spec--pad text num modifiers))
(when (> (length text) num)
(cond
((memq :chop-left modifiers)
(setq text (substring text (- (length text) num))))
((memq :chop-right modifiers)
(setq text (substring text 0 num))))))
(when (memq :uppercase modifiers)
(setq text (upcase text)))
(when (memq :lowercase modifiers)
(setq text (downcase text)))
;; Insert first, to preserve text properties.
(insert-and-inherit text)
;; Delete the specifier body.
(delete-region (+ (match-beginning 0) (length text))
(+ (match-end 0) (length text)))
;; Delete the percent sign.
(delete-region (1- (match-beginning 0)) (match-beginning 0))))))
;; Signal an error on bogus format strings.
(t
(unless only-present
(error "Invalid format string")))))
;; 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)))
(defun format-spec--pad (text total-length modifiers)
(if (> (length text) total-length)
;; The text is longer than the specified length; do nothing.
text
(let ((padding (make-string (- total-length (length text))
(if (memq :zero-pad modifiers)
?0
?\s))))
(if (memq :right-pad modifiers)
(concat text padding)
(concat padding text)))))
(defun format-spec--do-flags (str flags width trunc)
"Return STR formatted according to FLAGS, WIDTH, and TRUNC.
FLAGS is a list of keywords as returned by
`format-spec--parse-flags'. WIDTH and TRUNC are either nil or
string widths corresponding to `format-spec' modifiers."
(let (diff str-width)
;; Truncate original string first, like `format' does.
(when trunc
(setq str-width (string-width str))
(when (> (setq diff (- str-width trunc)) 0)
(setq str (if (memq :chop-left flags)
(truncate-string-to-width str str-width diff)
(format (format "%%.%ds" trunc) str))
;; We know the new width so save it for later.
str-width trunc)))
;; Pad or chop to width.
(when width
(setq str-width (or str-width (string-width str))
diff (- width str-width))
(cond ((zerop diff))
((> diff 0)
(let ((pad (make-string diff (if (memq :pad-zero flags) ?0 ?\s))))
(setq str (if (memq :pad-right flags)
(concat str pad)
(concat pad str)))))
((memq :chop-left flags)
(setq str (truncate-string-to-width str str-width (- diff))))
((memq :chop-right flags)
(setq str (format (format "%%.%ds" width) str))))))
;; Fiddle case.
(cond ((memq :upcase flags)
(upcase str))
((memq :downcase flags)
(downcase str))
(str)))
(defun format-spec--parse-modifiers (modifiers)
(defun format-spec--parse-flags (flags)
"Convert sequence of FLAGS to list of human-readable keywords."
(mapcan (lambda (char)
(when-let ((modifier
(pcase char
(?0 :zero-pad)
(?\s :space-pad)
(?^ :uppercase)
(?_ :lowercase)
(?- :right-pad)
(?< :chop-left)
(?> :chop-right))))
(list modifier)))
modifiers))
(pcase char
(?0 (list :pad-zero))
(?- (list :pad-right))
(?< (list :chop-left))
(?> (list :chop-right))
(?^ (list :upcase))
(?_ (list :downcase))))
flags))
(defun format-spec-make (&rest pairs)
"Return an alist suitable for use in `format-spec' based on PAIRS.
PAIRS is a list where every other element is a character and a value,
starting with a character."
PAIRS is a property list with characters as keys."
(let (alist)
(while pairs
(unless (cdr pairs)

View file

@ -29,8 +29,6 @@
(require 'gnus)
(require 'gnus-sum)
(require 'format-spec)
(autoload 'sieve-mode "sieve-mode")
(eval-when-compile
(require 'sieve))
@ -88,10 +86,10 @@ See the documentation for these variables and functions for details."
(save-buffer)
(shell-command
(format-spec gnus-sieve-update-shell-command
(format-spec-make ?f gnus-sieve-file
?s (or (cadr (gnus-server-get-method
nil gnus-sieve-select-method))
"")))))
`((?f . ,gnus-sieve-file)
(?s . ,(or (cadr (gnus-server-get-method
nil gnus-sieve-select-method))
""))))))
;;;###autoload
(defun gnus-sieve-generate ()

View file

@ -25,8 +25,6 @@
;;; Code:
(require 'format-spec)
(defcustom gssapi-program (list
(concat "gsasl %s %p "
"--mechanism GSSAPI "
@ -53,12 +51,9 @@ tried until a successful connection is made."
(coding-system-for-write 'binary)
(process (start-process
name buffer shell-file-name shell-command-switch
(format-spec
cmd
(format-spec-make
?s server
?p (number-to-string port)
?l user))))
(format-spec cmd `((?s . ,server)
(?p . ,(number-to-string port))
(?l . ,user)))))
response)
(when process
(while (and (memq (process-status process) '(open run))

View file

@ -24,7 +24,6 @@
;;; Code:
(require 'format-spec)
(eval-when-compile
(require 'cl-lib)
(require 'imap))
@ -769,14 +768,14 @@ Deleting old (> %s day(s)) incoming mail file `%s'." diff bfile)
"Fetcher for single-file sources."
(mail-source-bind (file source)
(mail-source-run-script
prescript (format-spec-make ?t mail-source-crash-box)
prescript `((?t . ,mail-source-crash-box))
prescript-delay)
(let ((mail-source-string (format "file:%s" path)))
(if (mail-source-movemail path mail-source-crash-box)
(prog1
(mail-source-callback callback path)
(mail-source-run-script
postscript (format-spec-make ?t mail-source-crash-box))
postscript `((?t . ,mail-source-crash-box)))
(mail-source-delete-crash-box))
0))))
@ -784,7 +783,7 @@ Deleting old (> %s day(s)) incoming mail file `%s'." diff bfile)
"Fetcher for directory sources."
(mail-source-bind (directory source)
(mail-source-run-script
prescript (format-spec-make ?t path) prescript-delay)
prescript `((?t . ,path)) prescript-delay)
(let ((found 0)
(mail-source-string (format "directory:%s" path)))
(dolist (file (directory-files
@ -793,7 +792,7 @@ Deleting old (> %s day(s)) incoming mail file `%s'." diff bfile)
(funcall predicate file)
(mail-source-movemail file mail-source-crash-box))
(cl-incf found (mail-source-callback callback file))
(mail-source-run-script postscript (format-spec-make ?t path))
(mail-source-run-script postscript `((?t . ,path)))
(mail-source-delete-crash-box)))
found)))
@ -803,8 +802,8 @@ Deleting old (> %s day(s)) incoming mail file `%s'." diff bfile)
;; fixme: deal with stream type in format specs
(mail-source-run-script
prescript
(format-spec-make ?p password ?t mail-source-crash-box
?s server ?P port ?u user)
`((?p . ,password) (?t . ,mail-source-crash-box)
(?s . ,server) (?P . ,port) (?u . ,user))
prescript-delay)
(let ((from (format "%s:%s:%s" server user port))
(mail-source-string (format "pop:%s@%s" user server))
@ -825,8 +824,8 @@ Deleting old (> %s day(s)) incoming mail file `%s'." diff bfile)
(mail-source-fetch-with-program
(format-spec
program
(format-spec-make ?p password ?t mail-source-crash-box
?s server ?P port ?u user))))
`((?p . ,password) (?t . ,mail-source-crash-box)
(?s . ,server) (?P . ,port) (?u . ,user)))))
(function
(funcall function mail-source-crash-box))
;; The default is to use pop3.el.
@ -863,8 +862,8 @@ Deleting old (> %s day(s)) incoming mail file `%s'." diff bfile)
(setq mail-source-new-mail-available nil))
(mail-source-run-script
postscript
(format-spec-make ?p password ?t mail-source-crash-box
?s server ?P port ?u user))
`((?p . ,password) (?t . ,mail-source-crash-box)
(?s . ,server) (?P . ,port) (?u . ,user)))
(mail-source-delete-crash-box)))
;; We nix out the password in case the error
;; was because of a wrong password being given.
@ -1077,8 +1076,9 @@ This only works when `display-time' is enabled."
"Fetcher for imap sources."
(mail-source-bind (imap source)
(mail-source-run-script
prescript (format-spec-make ?p password ?t mail-source-crash-box
?s server ?P port ?u user)
prescript
`((?p . ,password) (?t . ,mail-source-crash-box)
(?s . ,server) (?P . ,port) (?u . ,user))
prescript-delay)
(let ((from (format "%s:%s:%s" server user port))
(found 0)
@ -1143,8 +1143,8 @@ This only works when `display-time' is enabled."
(kill-buffer buf)
(mail-source-run-script
postscript
(format-spec-make ?p password ?t mail-source-crash-box
?s server ?P port ?u user))
`((?p . ,password) (?t . ,mail-source-crash-box)
(?s . ,server) (?P . ,port) (?u . ,user)))
found)))
(provide 'mail-source)

View file

@ -42,13 +42,12 @@
(require 'mail-parse)
(require 'mml)
(require 'rfc822)
(require 'format-spec)
(require 'dired)
(require 'mm-util)
(require 'rfc2047)
(require 'puny)
(require 'rmc) ; read-multiple-choice
(eval-when-compile (require 'subr-x)) ; when-let*
(require 'rmc) ; read-multiple-choice
(eval-when-compile (require 'subr-x))
(autoload 'mailclient-send-it "mailclient")
@ -440,8 +439,8 @@ whitespace)."
(defcustom message-elide-ellipsis "\n[...]\n\n"
"The string which is inserted for elided text.
This is a format-spec string, and you can use %l to say how many
lines were removed, and %c to say how many characters were
This is a `format-spec' string, and you can use %l to say how
many lines were removed, and %c to say how many characters were
removed."
:type 'string
:link '(custom-manual "(message)Various Commands")
@ -3977,7 +3976,6 @@ This function uses `mail-citation-hook' if that is non-nil."
"Cite function in the standard Message manner."
(message-cite-original-1 nil))
(autoload 'format-spec "format-spec")
(autoload 'gnus-date-get-time "gnus-util")
(defun message-insert-formatted-citation-line (&optional from date tz)
@ -4002,20 +4000,18 @@ See `message-citation-line-format'."
(when (or message-reply-headers (and from date))
(unless from
(setq from (mail-header-from message-reply-headers)))
(let* ((data (condition-case ()
(funcall (if (boundp 'gnus-extract-address-components)
gnus-extract-address-components
'mail-extract-address-components)
from)
(error nil)))
(let* ((data (ignore-errors
(funcall (or (bound-and-true-p
gnus-extract-address-components)
#'mail-extract-address-components)
from)))
(name (car data))
(fname name)
(lname name)
(net (car (cdr data)))
(name-or-net (or (car data)
(car (cdr data)) from))
(net (cadr data))
(name-or-net (or name net from))
(time
(when (string-match "%[^fnNFL]" message-citation-line-format)
(when (string-match-p "%[^FLNfn]" message-citation-line-format)
(cond ((numberp (car-safe date)) date) ;; backward compatibility
(date (gnus-date-get-time date))
(t
@ -4024,68 +4020,53 @@ See `message-citation-line-format'."
(tz (or tz
(when (stringp date)
(nth 8 (parse-time-string date)))))
(flist
(let ((i ?A) lst)
(when (stringp name)
;; Guess first name and last name:
(let* ((names (delq
nil
(mapcar
(lambda (x)
(if (string-match "\\`\\(\\w\\|[-.]\\)+\\'"
x)
x
nil))
(split-string name "[ \t]+"))))
(count (length names)))
(cond ((= count 1)
(setq fname (car names)
lname ""))
((or (= count 2) (= count 3))
(setq fname (car names)
lname (mapconcat 'identity (cdr names) " ")))
((> count 3)
(setq fname (mapconcat 'identity
(butlast names (- count 2))
" ")
lname (mapconcat 'identity
(nthcdr 2 names)
" "))))
(when (string-match "\\(.*\\),\\'" fname)
(let ((newlname (match-string 1 fname)))
(setq fname lname lname newlname)))))
;; The following letters are not used in `format-time-string':
(push ?E lst) (push "<E>" lst)
(push ?F lst) (push (or fname name-or-net) lst)
;; We might want to use "" instead of "<X>" later.
(push ?J lst) (push "<J>" lst)
(push ?K lst) (push "<K>" lst)
(push ?L lst) (push lname lst)
(push ?N lst) (push name-or-net lst)
(push ?O lst) (push "<O>" lst)
(push ?P lst) (push "<P>" lst)
(push ?Q lst) (push "<Q>" lst)
(push ?f lst) (push from lst)
(push ?i lst) (push "<i>" lst)
(push ?n lst) (push net lst)
(push ?o lst) (push "<o>" lst)
(push ?q lst) (push "<q>" lst)
(push ?t lst) (push "<t>" lst)
(push ?v lst) (push "<v>" lst)
;; Delegate the rest to `format-time-string':
(while (<= i ?z)
(when (and (not (memq i lst))
;; Skip (Z,a)
(or (<= i ?Z)
(>= i ?a)))
(push i lst)
(push (condition-case nil
(format-time-string (format "%%%c" i) time tz)
(error (format ">%c<" i)))
lst))
(setq i (1+ i)))
(reverse lst)))
(spec (apply 'format-spec-make flist)))
spec)
(when (stringp name)
;; Guess first name and last name:
(let* ((names (seq-filter
(lambda (s)
(string-match-p (rx bos (+ (in word ?. ?-)) eos) s))
(split-string name "[ \t]+")))
(count (length names)))
(cond ((= count 1)
(setq fname (car names)
lname ""))
((or (= count 2) (= count 3))
(setq fname (car names)
lname (string-join (cdr names) " ")))
((> count 3)
(setq fname (string-join (butlast names (- count 2))
" ")
lname (string-join (nthcdr 2 names) " "))))
(when (string-match "\\(.*\\),\\'" fname)
(let ((newlname (match-string 1 fname)))
(setq fname lname lname newlname)))))
;; The following letters are not used in `format-time-string':
(push (cons ?E "<E>") spec)
(push (cons ?F (or fname name-or-net)) spec)
;; We might want to use "" instead of "<X>" later.
(push (cons ?J "<J>") spec)
(push (cons ?K "<K>") spec)
(push (cons ?L lname) spec)
(push (cons ?N name-or-net) spec)
(push (cons ?O "<O>") spec)
(push (cons ?P "<P>") spec)
(push (cons ?Q "<Q>") spec)
(push (cons ?f from) spec)
(push (cons ?i "<i>") spec)
(push (cons ?n net) spec)
(push (cons ?o "<o>") spec)
(push (cons ?q "<q>") spec)
(push (cons ?t "<t>") spec)
(push (cons ?v "<v>") spec)
;; Delegate the rest to `format-time-string':
(dolist (c (nconc (number-sequence ?A ?Z)
(number-sequence ?a ?z)))
(unless (assq c spec)
(push (cons c (condition-case nil
(format-time-string (format "%%%c" c) time tz)
(error (format ">%c<" c))))
spec)))
(insert (format-spec message-citation-line-format spec)))
(newline)))

View file

@ -149,7 +149,6 @@
;;; Code:
(require 'dired)
(require 'format-spec)
(require 'image-mode)
(require 'widget)

View file

@ -25,7 +25,6 @@
;;; Code:
(require 'cl-lib)
(require 'format-spec)
(require 'shr)
(require 'url)
(require 'url-queue)

View file

@ -136,7 +136,6 @@
;;; Code:
(eval-when-compile (require 'cl-lib))
(require 'format-spec)
(require 'utf7)
(require 'rfc2104)
;; Hmm... digest-md5 is not part of Emacs.
@ -517,12 +516,9 @@ sure of changing the value of `foo'."
(process-connection-type imap-process-connection-type)
(process (start-process
name buffer shell-file-name shell-command-switch
(format-spec
cmd
(format-spec-make
?s server
?p (number-to-string port)
?l imap-default-user))))
(format-spec cmd `((?s . ,server)
(?p . ,(number-to-string port))
(?l . ,imap-default-user)))))
response)
(when process
(with-current-buffer buffer
@ -583,12 +579,9 @@ sure of changing the value of `foo'."
(process-connection-type imap-process-connection-type)
(process (start-process
name buffer shell-file-name shell-command-switch
(format-spec
cmd
(format-spec-make
?s server
?p (number-to-string port)
?l imap-default-user))))
(format-spec cmd `((?s . ,server)
(?p . ,(number-to-string port))
(?l . ,imap-default-user)))))
response)
(when process
(with-current-buffer buffer
@ -701,13 +694,10 @@ sure of changing the value of `foo'."
(process-connection-type imap-process-connection-type)
(process (start-process
name buffer shell-file-name shell-command-switch
(format-spec
cmd
(format-spec-make
?s server
?g imap-shell-host
?p (number-to-string port)
?l imap-default-user)))))
(format-spec cmd `((?s . ,server)
(?g . ,imap-shell-host)
(?p . ,(number-to-string port))
(?l . ,imap-default-user))))))
(when process
(while (and (memq (process-status process) '(open run))
(set-buffer buffer) ;; XXX "blue moon" nntp.el bug

View file

@ -170,8 +170,8 @@ a greeting from the server.
:nowait, if non-nil, says the connection should be made
asynchronously, if possible.
:shell-command is a format-spec string that can be used if :type
is `shell'. It has two specs, %s for host and %p for port
:shell-command is a `format-spec' string that can be used if
:type is `shell'. It has two specs, %s for host and %p for port
number. Example: \"ssh gateway nc %s %p\".
:tls-parameters is a list that should be supplied if you're
@ -453,11 +453,7 @@ gnutls-boot (as returned by `gnutls-boot-parameters')."
(network-stream-command stream capability-command eo-capa)
'tls)))))))
(declare-function format-spec "format-spec" (format spec))
(declare-function format-spec-make "format-spec" (&rest pairs))
(defun network-stream-open-shell (name buffer host service parameters)
(require 'format-spec)
(let* ((capability-command (plist-get parameters :capability-command))
(eoc (plist-get parameters :end-of-command))
(start (with-current-buffer buffer (point)))
@ -467,9 +463,8 @@ gnutls-boot (as returned by `gnutls-boot-parameters')."
shell-command-switch
(format-spec
(plist-get parameters :shell-command)
(format-spec-make
?s host
?p service))))))
`((?s . ,host)
(?p . ,service)))))))
(when coding (if (consp coding)
(set-process-coding-system stream
(car coding)

View file

@ -47,9 +47,6 @@
(require 'gnutls)
(autoload 'format-spec "format-spec")
(autoload 'format-spec-make "format-spec")
(defgroup tls nil
"Transport Layer Security (TLS) parameters."
:group 'comm)
@ -224,14 +221,11 @@ Fourth arg PORT is an integer specifying a port to connect to."
(while (and (not done) (setq cmd (pop cmds)))
(let ((process-connection-type tls-process-connection-type)
(formatted-cmd
(format-spec
cmd
(format-spec-make
?t (car (gnutls-trustfiles))
?h host
?p (if (integerp port)
(int-to-string port)
port)))))
(format-spec cmd `((?t . ,(car (gnutls-trustfiles)))
(?h . ,host)
(?p . ,(if (integerp port)
(number-to-string port)
port))))))
(message "Opening TLS connection with `%s'..." formatted-cmd)
(setq process (start-process
name buffer shell-file-name shell-command-switch

View file

@ -2295,9 +2295,6 @@ FILE is typically the output DVI or PDF file."
(setq uptodate nil)))))
uptodate)))
(autoload 'format-spec "format-spec")
(defvar tex-executable-cache nil)
(defun tex-executable-exists-p (name)
"Like `executable-find' but with a cache."

View file

@ -52,7 +52,7 @@
"Test `battery-format'."
(should (equal (battery-format "" ()) ""))
(should (equal (battery-format "" '((?b . "-"))) ""))
(should (equal (battery-format "%a%b%p%%" '((?b . "-") (?p . "99")))
"-99%")))
(should (equal (battery-format "%2a%-3b%.1p%%" '((?b . "-") (?p . "99")))
"- 9%")))
;;; battery-tests.el ends here

View file

@ -22,22 +22,145 @@
(require 'ert)
(require 'format-spec)
(ert-deftest test-format-spec ()
(ert-deftest format-spec-make ()
"Test `format-spec-make'."
(should-not (format-spec-make))
(should-error (format-spec-make ?b))
(should (equal (format-spec-make ?b "b") '((?b . "b"))))
(should-error (format-spec-make ?b "b" ?a))
(should (equal (format-spec-make ?b "b" ?a 'a)
'((?b . "b")
(?a . a)))))
(ert-deftest format-spec-parse-flags ()
"Test `format-spec--parse-flags'."
(should-not (format-spec--parse-flags nil))
(should-not (format-spec--parse-flags ""))
(should (equal (format-spec--parse-flags "-") '(:pad-right)))
(should (equal (format-spec--parse-flags " 0") '(:pad-zero)))
(should (equal (format-spec--parse-flags " -x0y< >^_z ")
'(:pad-right :pad-zero :chop-left :chop-right
:upcase :downcase))))
(ert-deftest format-spec-do-flags ()
"Test `format-spec--do-flags'."
(should (equal (format-spec--do-flags "" () nil nil) ""))
(dolist (flag '(:pad-zero :pad-right :upcase :downcase
:chop-left :chop-right))
(should (equal (format-spec--do-flags "" (list flag) nil nil) "")))
(should (equal (format-spec--do-flags "FOOBAR" '(:downcase :chop-right) 5 2)
" fo"))
(should (equal (format-spec--do-flags
"foobar" '(:pad-zero :pad-right :upcase :chop-left) 5 2)
"AR000")))
(ert-deftest format-spec-do-flags-truncate ()
"Test `format-spec--do-flags' truncation."
(let (flags)
(should (equal (format-spec--do-flags "" flags nil 0) ""))
(should (equal (format-spec--do-flags "" flags nil 1) ""))
(should (equal (format-spec--do-flags "a" flags nil 0) ""))
(should (equal (format-spec--do-flags "a" flags nil 1) "a"))
(should (equal (format-spec--do-flags "a" flags nil 2) "a"))
(should (equal (format-spec--do-flags "asd" flags nil 0) ""))
(should (equal (format-spec--do-flags "asd" flags nil 1) "a")))
(let ((flags '(:chop-left)))
(should (equal (format-spec--do-flags "" flags nil 0) ""))
(should (equal (format-spec--do-flags "" flags nil 1) ""))
(should (equal (format-spec--do-flags "a" flags nil 0) ""))
(should (equal (format-spec--do-flags "a" flags nil 1) "a"))
(should (equal (format-spec--do-flags "a" flags nil 2) "a"))
(should (equal (format-spec--do-flags "asd" flags nil 0) ""))
(should (equal (format-spec--do-flags "asd" flags nil 1) "d"))))
(ert-deftest format-spec-do-flags-pad ()
"Test `format-spec--do-flags' padding."
(let (flags)
(should (equal (format-spec--do-flags "" flags 0 nil) ""))
(should (equal (format-spec--do-flags "" flags 1 nil) " "))
(should (equal (format-spec--do-flags "a" flags 0 nil) "a"))
(should (equal (format-spec--do-flags "a" flags 1 nil) "a"))
(should (equal (format-spec--do-flags "a" flags 2 nil) " a")))
(let ((flags '(:pad-zero)))
(should (equal (format-spec--do-flags "" flags 0 nil) ""))
(should (equal (format-spec--do-flags "" flags 1 nil) "0"))
(should (equal (format-spec--do-flags "a" flags 0 nil) "a"))
(should (equal (format-spec--do-flags "a" flags 1 nil) "a"))
(should (equal (format-spec--do-flags "a" flags 2 nil) "0a")))
(let ((flags '(:pad-right)))
(should (equal (format-spec--do-flags "" flags 0 nil) ""))
(should (equal (format-spec--do-flags "" flags 1 nil) " "))
(should (equal (format-spec--do-flags "a" flags 0 nil) "a"))
(should (equal (format-spec--do-flags "a" flags 1 nil) "a"))
(should (equal (format-spec--do-flags "a" flags 2 nil) "a ")))
(let ((flags '(:pad-right :pad-zero)))
(should (equal (format-spec--do-flags "" flags 0 nil) ""))
(should (equal (format-spec--do-flags "" flags 1 nil) "0"))
(should (equal (format-spec--do-flags "a" flags 0 nil) "a"))
(should (equal (format-spec--do-flags "a" flags 1 nil) "a"))
(should (equal (format-spec--do-flags "a" flags 2 nil) "a0"))))
(ert-deftest format-spec-do-flags-chop ()
"Test `format-spec--do-flags' chopping."
(let ((flags '(:chop-left)))
(should (equal (format-spec--do-flags "a" flags 0 nil) ""))
(should (equal (format-spec--do-flags "a" flags 1 nil) "a"))
(should (equal (format-spec--do-flags "asd" flags 0 nil) ""))
(should (equal (format-spec--do-flags "asd" flags 1 nil) "d")))
(let ((flags '(:chop-right)))
(should (equal (format-spec--do-flags "a" flags 0 nil) ""))
(should (equal (format-spec--do-flags "a" flags 1 nil) "a"))
(should (equal (format-spec--do-flags "asd" flags 0 nil) ""))
(should (equal (format-spec--do-flags "asd" flags 1 nil) "a"))))
(ert-deftest format-spec-do-flags-case ()
"Test `format-spec--do-flags' case fiddling."
(dolist (flag '(:pad-zero :pad-right :chop-left :chop-right))
(let ((flags (list flag)))
(should (equal (format-spec--do-flags "a" flags nil nil) "a"))
(should (equal (format-spec--do-flags "A" flags nil nil) "A")))
(let ((flags (list flag :downcase)))
(should (equal (format-spec--do-flags "a" flags nil nil) "a"))
(should (equal (format-spec--do-flags "A" flags nil nil) "a")))
(let ((flags (list flag :upcase)))
(should (equal (format-spec--do-flags "a" flags nil nil) "A"))
(should (equal (format-spec--do-flags "A" flags nil nil) "A")))))
(ert-deftest format-spec ()
(should (equal (format-spec "" ()) ""))
(should (equal (format-spec "a" ()) "a"))
(should (equal (format-spec "b" '((?b . "bar"))) "b"))
(should (equal (format-spec "%%%b%%b%b%%" '((?b . "bar"))) "%bar%bbar%"))
(should (equal (format-spec "foo %b zot" `((?b . "bar")))
"foo bar zot"))
(should (equal (format-spec "foo %-10b zot" '((?b . "bar")))
"foo bar zot"))
(should (equal (format-spec "foo %10b zot" '((?b . "bar")))
"foo bar zot")))
"foo bar zot"))
(should (equal-including-properties
(format-spec (propertize "a" 'a 'b) '((?a . "foo")))
#("a" 0 1 (a b))))
(let ((fmt (concat (propertize "%a" 'a 'b)
(propertize "%%" 'c 'd)
"%b"
(propertize "%b" 'e 'f))))
(should (equal-including-properties
(format-spec fmt '((?b . "asd") (?a . "fgh")))
#("fgh%asdasd" 0 3 (a b) 3 4 (c d) 7 10 (e f))))))
(ert-deftest test-format-unknown ()
(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"))))
(should (equal (format-spec "foo %b %z zot" '((?b . "bar")) t)
"foo bar %z zot"))
(should (equal (format-spec "foo %b %z %% zot" '((?b . "bar")) t)
"foo bar %z %% zot")))
(should (equal (format-spec "foo %4b %%%4z %%4 zot" '((?b . "bar")) t)
"foo bar %%%4z %%4 zot"))
(should (equal (format-spec "foo %4b %%%4z %%4 zot" '((?b . "bar")) 'ignore)
"foo bar %%4z %4 zot"))
(should (equal (format-spec "foo %4b %%%4z %%4 zot" '((?b . "bar")) 'delete)
"foo bar % %4 zot")))
(ert-deftest test-format-modifiers ()
(ert-deftest format-spec-flags ()
(should (equal (format-spec "foo %10b zot" '((?b . "bar")))
"foo bar zot"))
(should (equal (format-spec "foo % 10b zot" '((?b . "bar")))