From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.ciao.gmane.io!not-for-mail From: "Basil L. Contovounesios" Newsgroups: gmane.emacs.bugs Subject: bug#41758: 28.0.50; Fix and extend format-spec Date: Mon, 08 Jun 2020 00:50:22 +0100 Message-ID: <878sgyl9kx.fsf@tcd.ie> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Injection-Info: ciao.gmane.io; posting-host="ciao.gmane.io:159.69.161.202"; logging-data="83194"; mail-complaints-to="usenet@ciao.gmane.io" User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/28.0.50 (gnu/linux) To: 41758@debbugs.gnu.org Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Mon Jun 08 01:51:14 2020 Return-path: Envelope-to: geb-bug-gnu-emacs@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1ji54L-000Lb3-QY for geb-bug-gnu-emacs@m.gmane-mx.org; Mon, 08 Jun 2020 01:51:14 +0200 Original-Received: from localhost ([::1]:35568 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1ji54K-0004IT-BX for geb-bug-gnu-emacs@m.gmane-mx.org; Sun, 07 Jun 2020 19:51:12 -0400 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]:54574) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1ji54B-0004IL-5P for bug-gnu-emacs@gnu.org; Sun, 07 Jun 2020 19:51:03 -0400 Original-Received: from debbugs.gnu.org ([209.51.188.43]:43678) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1ji54A-0006Du-N5 for bug-gnu-emacs@gnu.org; Sun, 07 Jun 2020 19:51:02 -0400 Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1ji54A-0002tG-Lo for bug-gnu-emacs@gnu.org; Sun, 07 Jun 2020 19:51:02 -0400 X-Loop: help-debbugs@gnu.org Resent-From: "Basil L. Contovounesios" Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Sun, 07 Jun 2020 23:51:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: report 41758 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: patch X-Debbugs-Original-To: bug-gnu-emacs@gnu.org Original-Received: via spool by submit@debbugs.gnu.org id=B.159157383711075 (code B ref -1); Sun, 07 Jun 2020 23:51:02 +0000 Original-Received: (at submit) by debbugs.gnu.org; 7 Jun 2020 23:50:37 +0000 Original-Received: from localhost ([127.0.0.1]:55224 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1ji53j-0002sY-7j for submit@debbugs.gnu.org; Sun, 07 Jun 2020 19:50:37 -0400 Original-Received: from lists.gnu.org ([209.51.188.17]:37862) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1ji53f-0002sP-T0 for submit@debbugs.gnu.org; Sun, 07 Jun 2020 19:50:33 -0400 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]:54532) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1ji53f-0004Ax-Jm for bug-gnu-emacs@gnu.org; Sun, 07 Jun 2020 19:50:31 -0400 Original-Received: from mail-wm1-x333.google.com ([2a00:1450:4864:20::333]:56024) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1ji53b-0005xT-St for bug-gnu-emacs@gnu.org; Sun, 07 Jun 2020 19:50:31 -0400 Original-Received: by mail-wm1-x333.google.com with SMTP id c71so13607307wmd.5 for ; Sun, 07 Jun 2020 16:50:26 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=tcd-ie.20150623.gappssmtp.com; s=20150623; h=from:to:subject:date:message-id:user-agent:mime-version; bh=KSDboJ7Ej/SSdn1lh8gKzwlRccOAoqk49qoxZpCkY/4=; b=hmYuQo6vLbeo0ceu1WHqwWTbleiniwUrtcivnDywqKKS8fk/sFErd613lpE2XpaD61 J2KX2brc2XHMeerkAUmaxV1ouX75nITdKlFf3jHRAphZv3wA0c0BvuSEBPLuE4wncXYX AnwRvlUTGQi2hGLL90kYpSz366dMSWpz1bZYcXK7EXwl2QRB4NVpkLvwhTxE9/JE4bC2 1+Ktj+SHC72ujEdXM968kec6RG1hj/xjzBbYXDAYfV8JpjElgRAd5GFZjycVFFUEvivH a/EOxYQwrZXIgK1WEbjX++TszCNzHB9U/U87gQG2K18/tGRrVBtasCnK6QO4kuDsJzeY EDJQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:user-agent :mime-version; bh=KSDboJ7Ej/SSdn1lh8gKzwlRccOAoqk49qoxZpCkY/4=; b=qLM53NKdTxIYyCPcyrUoiCk3QfnoXCRyJrVQZsMbpIkjTvCbRbKkshMvJ6ZNIWvTb3 T3lXJk17AhzAvG+12jyIpgqvXLOFo/iiLMqYYS6mnMm7d3tSnPrFFKjszbKlampbJEiA Dv+PkDa0X+nBH3UK85bFmdwunVYINd345bUb/G6b0n7etyiGGdOURq5074jAlmrOiLxd tKS+XbH8Zd+rsSdZPjJf/A1UXAkFnSWEfLCivNx2ksv+OCwKJlF457v+xQDlHH3MBpzk VnUHFfRX/vaUGFlPcNoTeVZSGizj7LD+Gi4Ffd8KhBbaOTUnYeFscxRpOG+SKqGtY1Cv TNBw== X-Gm-Message-State: AOAM532YajPhGRWcQGh6J8B8Cmrpx05x7K0o6Pa1chQhczeH2eGtCKdJ ny+1vXeUTcFIjJWaNVdxO51wlyywmeb0Iw== X-Google-Smtp-Source: ABdhPJxH9qGJ8fqQWu99zip1WU+rNGhf6kqko9xrxqj+VlEmmjDQTIJ2ybX6YoU9N21rZb7Qw7BjBA== X-Received: by 2002:a1c:a74f:: with SMTP id q76mr14550547wme.65.1591573825111; Sun, 07 Jun 2020 16:50:25 -0700 (PDT) Original-Received: from localhost ([2a02:8084:20e2:c380:92bd:1bfd:38fc:fae2]) by smtp.gmail.com with ESMTPSA id l1sm22923050wrb.31.2020.06.07.16.50.23 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 07 Jun 2020 16:50:24 -0700 (PDT) Received-SPF: none client-ip=2a00:1450:4864:20::333; envelope-from=contovob@tcd.ie; helo=mail-wm1-x333.google.com X-detected-operating-system: by eggs.gnu.org: No matching host in p0f cache. That's all we know. X-Spam_score_int: -18 X-Spam_score: -1.9 X-Spam_bar: - X-Spam_report: (-1.9 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, URIBL_BLOCKED=0.001 autolearn=_AUTOLEARN X-Spam_action: no action X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-BeenThere: bug-gnu-emacs@gnu.org List-Id: "Bug reports for GNU Emacs, the Swiss army knife of text editors" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Original-Sender: "bug-gnu-emacs" Xref: news.gmane.io gmane.emacs.bugs:181721 Archived-At: --=-=-= Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable Tags: patch I attach a patch which fixes and extends some of the changes made to format-spec's behaviour in Emacs 27. Some of the proposed changes were already alluded to in the discussion of bug#41571. --- Before Emacs 27, format-spec was implemented in terms of 'format', allowing e.g.: (format-spec "%.2s" '((?s . "=D7=A9=D6=B8=D7=81=D7=9C=D7=95=D6=B9=D7=9D")= )) ; =3D> "=D7=A9=D6=B8=D7=81=D7=9C" In Emacs 27, a precision modifier ".N" is still supported (doesn't signal an error), but is a no-op: (format-spec "%.2s" '((?s . "=D7=A9=D6=B8=D7=81=D7=9C=D7=95=D6=B9=D7=9D")= )) ; =3D> "=D7=A9=D6=B8=D7=81=D7=9C=D7=95=D6=B9=D7=9D" This is arguably a regression (since format-spec's docstring historically said it supported "'format'-like specs" without explicitly listing the precision modifier as one of them). I wouldn't be surprised if someone misses this feature. Instead, Emacs 27 added the notion of "padding or truncating" (as a single operation) to a desired length: (format-spec "%>2s" '((?s . "=D7=A9=D6=B8=D7=81=D7=9C=D7=95=D6=B9=D7=9D")= )) ; =3D> "=D7=A9=D6=B8" There are two problems with this: 0. Unlike 'format', it truncates to length rather than width. 1. It is impossible to specify separate padding and truncation, e.g.: (format "%3.2s" "=D7=A9=D6=B8=D7=81=D7=9C=D7=95=D6=B9=D7=9D") ; =3D> " = =D7=A9=D6=B8=D7=81=D7=9C" The attached patch brings back 'format'-like truncation based on string width, and separate from padding: (format-spec "%>2s" '((?s . "=D7=A9=D6=B8=D7=81=D7=9C=D7=95=D6=B9=D7=9D")= )) ; =3D> "=D7=A9=D6=B8=D7=81=D7=9C" (format-spec "%3.2s" '((?s . "=D7=A9=D6=B8=D7=81=D7=9C=D7=95=D6=B9=D7=9D"= ))) ; =3D> " =D7=A9=D6=B8=D7=81=D7=9C" (let ((f "%3.2s") (s "=D7=A9=D6=B8=D7=81=D7=9C=D7=95=D6=B9=D7=9D")) (equal (format-spec f `((?s . ,s))) (format f s))) ; =3D> t --- Emacs 27 also added an optional third argument for ignoring the case when the caller does not provide a particular replacement, e.g.: (format-spec "%s" ()) ; =3D> (error "Invalid format character: =E2=80= =98%s=E2=80=99") (format-spec "%s" () t) ; =3D> "%s" The problem with a non-nil third argument is that it also unconditionally leaves '%%' verbatim in the output: (format-spec "%%%s" () t) ; =3D> "%%%s" I'm sure this has its uses, but I find it a surprising default since the replacement of '%%' is always known. The function battery-format in lisp/battery.el is an example of where the usual replacement of '%%' is desirable, even when some replacements are not provided. The attached patch therefore adds two new special values to the optional third argument: (format-spec "%%%s" () 'ignore) ; =3D> "%%s" (format-spec "%%%s" () 'delete) ; =3D> "%" Together with the Emacs 27 behaviour, I think these should cover most bases. WDYT? Thanks, --=20 Basil --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=0001-Fix-and-extend-format-spec.patch >From 824d1df77e95651904b4f8a97cd4be9db255e098 Mon Sep 17 00:00:00 2001 From: "Basil L. Contovounesios" Date: Fri, 29 May 2020 19:56:14 +0100 Subject: [PATCH] Fix and extend format-spec * 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. Add 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/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. --- doc/lispref/strings.texi | 35 +++++-- etc/NEWS | 17 ++++ lisp/battery.el | 18 ++-- lisp/dired-aux.el | 15 ++- lisp/erc/erc-match.el | 19 ++-- lisp/erc/erc.el | 21 ++-- lisp/format-spec.el | 181 ++++++++++++++++++--------------- lisp/gnus/gnus-sieve.el | 10 +- lisp/gnus/gssapi.el | 11 +- lisp/gnus/mail-source.el | 30 +++--- lisp/gnus/message.el | 137 +++++++++++-------------- lisp/image-dired.el | 1 - lisp/net/eww.el | 1 - lisp/net/imap.el | 30 ++---- lisp/net/network-stream.el | 13 +-- lisp/obsolete/tls.el | 16 +-- lisp/textmodes/tex-mode.el | 3 - test/lisp/format-spec-tests.el | 135 ++++++++++++++++++++++-- 18 files changed, 405 insertions(+), 288 deletions(-) diff --git a/doc/lispref/strings.texi b/doc/lispref/strings.texi index 4a7bda57c4..2ef88b9025 100644 --- a/doc/lispref/strings.texi +++ b/doc/lispref/strings.texi @@ -1152,7 +1152,7 @@ Custom Format Strings 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 @@ Custom Format Strings 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 @@ Custom Format Strings @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 @@ Custom Format Strings 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 @@ Custom Format Strings @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: diff --git a/etc/NEWS b/etc/NEWS index 6e94d4a91b..add842a7ec 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -451,6 +451,16 @@ https://www.w3.org/TR/xml/#charsets). Now it rejects such strings. ** The metamail.el library is now marked obsolete. +** 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 @@ -568,6 +578,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 diff --git a/lisp/battery.el b/lisp/battery.el index b8855a8ce3..3872819650 100644 --- a/lisp/battery.el +++ b/lisp/battery.el @@ -121,7 +121,10 @@ battery-echo-area-format %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 @@ battery-mode-line-format %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 @@ battery-pmset (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. diff --git a/lisp/dired-aux.el b/lisp/dired-aux.el index 24ebfa4b0d..97d4893239 100644 --- a/lisp/dired-aux.el +++ b/lisp/dired-aux.el @@ -1064,8 +1064,6 @@ dired-compress-files-alist 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 @@ dired-do-compress-to 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 @@ dired-do-compress-to (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)) diff --git a/lisp/erc/erc-match.el b/lisp/erc/erc-match.el index 3107ff2ccd..0e98f2bc61 100644 --- a/lisp/erc/erc-match.el +++ b/lisp/erc/erc-match.el @@ -555,16 +555,15 @@ erc-log-matches (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)) diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el index cfde84e19a..3880778794 100644 --- a/lisp/erc/erc.el +++ b/lisp/erc/erc.el @@ -6391,17 +6391,16 @@ tabbar--local-hlf (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") diff --git a/lisp/format-spec.el b/lisp/format-spec.el index 9278bd74c4..6af79a4416 100644 --- a/lisp/format-spec.el +++ b/lisp/format-spec.el @@ -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 @@ format-spec \\=`((?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: - %character + %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 @@ format-spec 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) diff --git a/lisp/gnus/gnus-sieve.el b/lisp/gnus/gnus-sieve.el index 278e3a5d6f..5d8f9b55de 100644 --- a/lisp/gnus/gnus-sieve.el +++ b/lisp/gnus/gnus-sieve.el @@ -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 @@ gnus-sieve-update (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 () diff --git a/lisp/gnus/gssapi.el b/lisp/gnus/gssapi.el index 218a1542e3..485d58ad94 100644 --- a/lisp/gnus/gssapi.el +++ b/lisp/gnus/gssapi.el @@ -25,8 +25,6 @@ ;;; Code: -(require 'format-spec) - (defcustom gssapi-program (list (concat "gsasl %s %p " "--mechanism GSSAPI " @@ -53,12 +51,9 @@ open-gssapi-stream (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)) diff --git a/lisp/gnus/mail-source.el b/lisp/gnus/mail-source.el index acf35a376a..43180726c4 100644 --- a/lisp/gnus/mail-source.el +++ b/lisp/gnus/mail-source.el @@ -24,7 +24,6 @@ ;;; Code: -(require 'format-spec) (eval-when-compile (require 'cl-lib) (require 'imap)) @@ -769,14 +768,14 @@ mail-source-fetch-file "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 @@ mail-source-fetch-directory "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 @@ mail-source-fetch-directory (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 @@ mail-source-fetch-pop ;; 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 @@ mail-source-fetch-pop (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 @@ mail-source-fetch-pop (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 @@ mail-source-fetch-imap "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 @@ mail-source-fetch-imap (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) diff --git a/lisp/gnus/message.el b/lisp/gnus/message.el index 5a6827af76..fb560f0eab 100644 --- a/lisp/gnus/message.el +++ b/lisp/gnus/message.el @@ -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 @@ message-signature-separator (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 @@ message-cite-original "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 @@ message-insert-formatted-citation-line (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 @@ message-insert-formatted-citation-line (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 "" lst) - (push ?F lst) (push (or fname name-or-net) lst) - ;; We might want to use "" instead of "" later. - (push ?J lst) (push "" lst) - (push ?K lst) (push "" lst) - (push ?L lst) (push lname lst) - (push ?N lst) (push name-or-net lst) - (push ?O lst) (push "" lst) - (push ?P lst) (push "

" lst) - (push ?Q lst) (push "" lst) - (push ?f lst) (push from lst) - (push ?i lst) (push "" lst) - (push ?n lst) (push net lst) - (push ?o lst) (push "" lst) - (push ?q lst) (push "" lst) - (push ?t lst) (push "" lst) - (push ?v lst) (push "" 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 "") spec) + (push (cons ?F (or fname name-or-net)) spec) + ;; We might want to use "" instead of "" later. + (push (cons ?J "") spec) + (push (cons ?K "") spec) + (push (cons ?L lname) spec) + (push (cons ?N name-or-net) spec) + (push (cons ?O "") spec) + (push (cons ?P "

") spec) + (push (cons ?Q "") spec) + (push (cons ?f from) spec) + (push (cons ?i "") spec) + (push (cons ?n net) spec) + (push (cons ?o "") spec) + (push (cons ?q "") spec) + (push (cons ?t "") spec) + (push (cons ?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))) diff --git a/lisp/image-dired.el b/lisp/image-dired.el index 1cc38ba714..6f297672ca 100644 --- a/lisp/image-dired.el +++ b/lisp/image-dired.el @@ -149,7 +149,6 @@ ;;; Code: (require 'dired) -(require 'format-spec) (require 'image-mode) (require 'widget) diff --git a/lisp/net/eww.el b/lisp/net/eww.el index 2a70560ca7..cf31d37f07 100644 --- a/lisp/net/eww.el +++ b/lisp/net/eww.el @@ -25,7 +25,6 @@ ;;; Code: (require 'cl-lib) -(require 'format-spec) (require 'shr) (require 'url) (require 'url-queue) diff --git a/lisp/net/imap.el b/lisp/net/imap.el index aa10f0291f..a492dc8c79 100644 --- a/lisp/net/imap.el +++ b/lisp/net/imap.el @@ -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 @@ imap-kerberos4-open (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 @@ imap-gssapi-open (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 @@ imap-shell-open (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 diff --git a/lisp/net/network-stream.el b/lisp/net/network-stream.el index 1d5cf382a8..1c371f5987 100644 --- a/lisp/net/network-stream.el +++ b/lisp/net/network-stream.el @@ -170,8 +170,8 @@ open-network-stream :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 @@ network-stream-open-tls (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 @@ network-stream-open-shell 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) diff --git a/lisp/obsolete/tls.el b/lisp/obsolete/tls.el index cd091c0108..d1b215cbfb 100644 --- a/lisp/obsolete/tls.el +++ b/lisp/obsolete/tls.el @@ -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 @@ open-tls-stream (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 diff --git a/lisp/textmodes/tex-mode.el b/lisp/textmodes/tex-mode.el index 1b302e34a7..e3d5759579 100644 --- a/lisp/textmodes/tex-mode.el +++ b/lisp/textmodes/tex-mode.el @@ -2295,9 +2295,6 @@ tex-uptodate-p (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." diff --git a/test/lisp/format-spec-tests.el b/test/lisp/format-spec-tests.el index 23ee88c526..11882217af 100644 --- a/test/lisp/format-spec-tests.el +++ b/test/lisp/format-spec-tests.el @@ -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"))) -- 2.26.2 --=-=-=--