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#41571: 27.0.91; "(elisp) Interpolated Strings" is under "(elisp) Text" Date: Fri, 29 May 2020 19:35:31 +0100 Message-ID: <877dwu4mj0.fsf@tcd.ie> References: <877dwxexsh.fsf@tcd.ie> <83d06osfyw.fsf@gnu.org> <87zh9sfij1.fsf@tcd.ie> <837dwws38a.fsf@gnu.org> 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="105529"; mail-complaints-to="usenet@ciao.gmane.io" User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/28.0.50 (gnu/linux) Cc: 41571@debbugs.gnu.org To: Eli Zaretskii Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Fri May 29 20:36:13 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 1jejrY-000RPq-Gh for geb-bug-gnu-emacs@m.gmane-mx.org; Fri, 29 May 2020 20:36:12 +0200 Original-Received: from localhost ([::1]:44146 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1jejrX-00082h-4Q for geb-bug-gnu-emacs@m.gmane-mx.org; Fri, 29 May 2020 14:36:11 -0400 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]:54288) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1jejrO-00082Y-Pw for bug-gnu-emacs@gnu.org; Fri, 29 May 2020 14:36:02 -0400 Original-Received: from debbugs.gnu.org ([209.51.188.43]:44678) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1jejrO-0007an-Gi for bug-gnu-emacs@gnu.org; Fri, 29 May 2020 14:36:02 -0400 Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1jejrO-0003I4-EF for bug-gnu-emacs@gnu.org; Fri, 29 May 2020 14:36: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: Fri, 29 May 2020 18:36:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 41571 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: patch Original-Received: via spool by 41571-submit@debbugs.gnu.org id=B41571.159077734212617 (code B ref 41571); Fri, 29 May 2020 18:36:02 +0000 Original-Received: (at 41571) by debbugs.gnu.org; 29 May 2020 18:35:42 +0000 Original-Received: from localhost ([127.0.0.1]:56224 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1jejr3-0003HQ-Ro for submit@debbugs.gnu.org; Fri, 29 May 2020 14:35:42 -0400 Original-Received: from mail-wm1-f54.google.com ([209.85.128.54]:37177) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1jejr1-0003HC-Dp for 41571@debbugs.gnu.org; Fri, 29 May 2020 14:35:40 -0400 Original-Received: by mail-wm1-f54.google.com with SMTP id f5so4886291wmh.2 for <41571@debbugs.gnu.org>; Fri, 29 May 2020 11:35:39 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=tcd-ie.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:references:date:in-reply-to:message-id :user-agent:mime-version; bh=8I+H//YGfW9ir6jmEcN763PFz54Rv8nHC7NlZ3L7iPY=; b=IppET66+yOCsscP8C/FhmmV7u6I8+JABs2y2iVG8rrHtycy5jE0FDBdn3aTZ9pQ5kc KNpMp1UT5mZmOd4/3A6uoqIVyTsrf7151yDdEjIsnJtcXvFuMKj4BiZAk6I/3+muFG7V m5xfztkLC79+SP56dEnivuWvGxjA0uH1wV4X9UBT2FzFA+XVVVLo1iTUeEsMa4HKcFCH RNpBYM4klnOs25p8wbKxauqO4QC2vN+agJ9GbC1I5DjtgCWiLeEe24KbM1/KYZSJMbh9 LHF9347agR2cAYX8Xr3ZioyL4a78wEcm7EFjWmtHY76yTPiSWNApY5dnnnbzwQ+Tkpfa cGgw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:references:date:in-reply-to :message-id:user-agent:mime-version; bh=8I+H//YGfW9ir6jmEcN763PFz54Rv8nHC7NlZ3L7iPY=; b=F9Yu0+Y3wjxvqzXLQvGb2MMdhHpEPkNoQSnDkTQCOYvkKlsCcZMx6jsVHzUoQ0nk6I Nqji1sRVOBEmGJbaWrWbudxN+frgiVy/dKrkpaY4j+2EBgk3qGb3hJm8maZXywEq3HC2 Ai3HSFbO50184HsBWHmiAo4LlgooYK1q3ZpNjajUHxQyCXaOZXugDUBIECZI4CfPOP0p eZk9MvvKCYVNlqSCkUDzJ244oblfFQqYV+SP1eRwCEO/vNmQNCLeapRqZI/ura7LaWhj DPaU8qfafLFa0nLO9OeIJRCNoqVukZRDuJkmHpE+SBvxelu9D57rRVdI6uttIGSnJL3e y/Aw== X-Gm-Message-State: AOAM53317Vqzpn/ZHd737QhNAN/CGp29ZlqHSRCRsSOXATqREcIDDlma MYLOSBk9YOYEA1C3+X2LuqhrXQ== X-Google-Smtp-Source: ABdhPJz5iVlgXlr6nmjSkhskoy9Ars7j2yAQd3rlQXTMGEq8IdyJYrDAQlHf2kuQfK3FJJgU+Drbpg== X-Received: by 2002:a1c:2644:: with SMTP id m65mr9592707wmm.178.1590777333310; Fri, 29 May 2020 11:35:33 -0700 (PDT) Original-Received: from localhost ([2a02:8084:20e2:c380:1f68:7ff5:120d:64e]) by smtp.gmail.com with ESMTPSA id r4sm10882103wro.32.2020.05.29.11.35.32 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 29 May 2020 11:35:32 -0700 (PDT) In-Reply-To: <837dwws38a.fsf@gnu.org> (Eli Zaretskii's message of "Thu, 28 May 2020 14:33:57 +0300") 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:181199 Archived-At: --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=0001-Improve-format-spec-documentation-bug-41571.patch >From b6a7bfbbb08d5c09e85f50b9100b39c1e1645bc2 Mon Sep 17 00:00:00 2001 From: "Basil L. Contovounesios" Date: Thu, 28 May 2020 00:53:42 +0100 Subject: [PATCH] Improve format-spec documentation (bug#41571) * doc/lispref/text.texi (Interpolated Strings): Move from here... * doc/lispref/strings.texi (Custom Format Strings): ...to here, renaming the node and clarifying the documentation. (Formatting Strings): End node with sentence referring to the next one. * lisp/format-spec.el (format-spec): Clarify docstring. --- doc/lispref/strings.texi | 138 +++++++++++++++++++++++++++++++++++++++ doc/lispref/text.texi | 64 ------------------ lisp/format-spec.el | 49 ++++++++------ 3 files changed, 168 insertions(+), 83 deletions(-) diff --git a/doc/lispref/strings.texi b/doc/lispref/strings.texi index 70c3b3cf4b..0c750a8143 100644 --- a/doc/lispref/strings.texi +++ b/doc/lispref/strings.texi @@ -28,6 +28,7 @@ Strings and Characters * Text Comparison:: Comparing characters or strings. * String Conversion:: Converting to and from characters and strings. * Formatting Strings:: @code{format}: Emacs's analogue of @code{printf}. +* Custom Format Strings:: Formatting custom @code{format} specifications. * Case Conversion:: Case conversion functions. * Case Tables:: Customizing case conversion. @end menu @@ -1122,6 +1123,143 @@ Formatting Strings NaNs and can lose precision and type, and @samp{#x%x} and @samp{#o%o} can mishandle negative integers. @xref{Input Functions}. +The functions described in this section accept a fixed set of +specification characters. The next section describes a function +@code{format-spec} which accepts custom specification characters. + +@node Custom Format Strings +@section Custom Format Strings +@cindex custom format string +@cindex custom @samp{%}-sequence in format + +It is, in some circumstances, useful to allow users to control how +certain text is generated via custom format control strings. For +example, a format string could control how to display someone's +forename, surname, and email address. Using the function +@code{format} described in the previous section, the format string +could be something like @code{"%s %s <%s>"}. This approach quickly +becomes impractical, however, as it can be unclear which specification +character corresponds to which piece of information. + +A more convenient format string for such cases would be something like +@code{"%f %l <%e>"}, where each specification character carries more +semantic information and can easily be rearranged relative to other +specification characters. The function @code{format-spec} described +in this section performs a similar function to @code{format}, except +it operates on format control strings that comprise arbitrary +specification characters. + +@defun format-spec format specification &optional only-present +This function returns a string equal to the format control string +@var{format}, replacing any format specifications it contains with +values found in the alist @var{specification} (@pxref{Association +Lists}). + +Each key in @var{specification} is a format specification character, +and its associated value is the string to replace it with. For +example, an alist entry @code{(?a . "alpha")} means to replace any +@samp{%a} specifications in @var{format} with @samp{alpha}. + +The characters in @var{format}, other than the format specifications, +are copied directly into the output, including their text properties, +if any. Any text properties of the format specifications are copied +to their substitutions. + +Some useful properties are gained as a result of @var{specification} +being an alist. The alist may contain more unique keys than there are +unique specification characters in @var{format}; unused keys are +simply ignored. If the same key is contained more than once, the +first one found is used. If @var{format} contains the same format +specification character more than once, then the same value found in +@var{specification} is used as a basis for all of that character's +substitutions. + +The optional argument @var{only-present} indicates how to handle +format specification characters in @var{format} that are not found in +@var{specification}. If it is @code{nil} or omitted, an error is +emitted. Otherwise, those format specifications and any occurrences +of @samp{%%} in @var{format} are left verbatim in the output, +including their text properties, if any. +@end defun + +The syntax of format specifications accepted by @code{format-spec} is +similar, but not identical, to that accepted by @code{format}. In +both cases, a format specification is a sequence of characters +beginning with @samp{%} and ending with an alphabetic letter such as +@samp{s}. The only exception to this is the specification @samp{%%}, +which is replaced with a single @samp{%}. + +Unlike @code{format}, which assigns specific meanings to a fixed set +of specification characters, @code{format-spec} accepts arbitrary +specification characters and treats them all equally. For example: + +@example +(format-spec "su - %u %l" + `((?u . ,(user-login-name)) + (?l . "ls"))) + @result{} "su - foo ls" +@end example + +A format specification can include any number of the following flag +characters immediately after the @samp{%} to modify aspects of the +substitution. + +@table @samp +@item 0 +This flag causes any padding inserted by the width, if specified, to +consist of @samp{0} characters instead of spaces. + +@item - +This flag causes any padding inserted by the width, if specified, to +be inserted on the right rather than the left. + +@item < +This flag causes the substitution to be truncated to the given width, +if specified, by removing characters from the left. + +@item > +This flag causes the substitution to be truncated to the given width, +if specified, by removing characters from the right. + +@item ^ +This flag converts the substituted text to upper case (@pxref{Case +Conversion}). + +@item _ +This flag converts the substituted text to lower case (@pxref{Case +Conversion}). +@end table + +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 +extended with padding, normally comprising spaces inserted on the +left: + +@example +(format-spec "%8a is padded on the left with spaces" + '((?a . "alpha"))) + @result{} " alpha is padded on the left with spaces" +@end example + +Here is a more complicated example that combines several +aforementioned features: + +@example +(format-spec "%<06e %<06b" + '((?b . "beta") + (?e . "epsilon"))) + @result{} "psilon 00beta" +@end example + +This format string means ``substitute into the output the values +associated with @code{?e} and @code{?b} in the given alist, either +padding them with leading zeros or truncating leading characters until +they're each six characters wide.'' + @node Case Conversion @section Case Conversion in Lisp @cindex upper case diff --git a/doc/lispref/text.texi b/doc/lispref/text.texi index de436fa9e6..a14867e1d1 100644 --- a/doc/lispref/text.texi +++ b/doc/lispref/text.texi @@ -58,7 +58,6 @@ Text of another buffer. * Decompression:: Dealing with compressed data. * Base 64:: Conversion to or from base 64 encoding. -* Interpolated Strings:: Formatting Customizable Strings. * Checksum/Hash:: Computing cryptographic hashes. * GnuTLS Cryptography:: Cryptographic algorithms imported from GnuTLS. * Parsing HTML/XML:: Parsing HTML and XML. @@ -4662,69 +4661,6 @@ Base 64 is optional, and the URL variant of base 64 encoding is used. @end defun - -@node Interpolated Strings -@section Formatting Customizable Strings - -It is, in some circumstances, useful to present users with a string to -be customized that can then be expanded programmatically. For -instance, @code{erc-header-line-format} is @code{"%n on %t (%m,%l) -%o"}, and each of those characters after the percent signs are -expanded when the header line is computed. To do this, the -@code{format-spec} function is used: - -@defun format-spec format specification &optional only-present -@var{format} is the format specification string as in the example -above. @var{specification} is an alist that has elements where the -@code{car} is a character and the @code{cdr} is the substitution. - -If @var{only-present} is @code{nil}, errors will be signaled if a -format character has been used that's not present in -@var{specification}. If it's non-@code{nil}, that format -specification is left verbatim in the result. -@end defun - -Here's a trivial example: - -@example -(format-spec "su - %u %l" - `((?u . ,(user-login-name)) - (?l . "ls"))) - @result{} "su - foo ls" -@end example - -In addition to allowing padding/limiting to a certain length, the -following modifiers can be used: - -@table @asis -@item @samp{0} -Pad with zeros instead of the default spaces. - -@item @samp{-} -Pad to the right. - -@item @samp{^} -Use upper case. - -@item @samp{_} -Use lower case. - -@item @samp{<} -If the length needs to be limited, remove characters from the left. - -@item @samp{>} -Same as previous, but remove characters from the right. -@end table - -If contradictory modifiers are used (for instance, both upper and -lower case), then what happens is undefined. - -As an example, @samp{"%<010b"} means ``insert the @samp{b} expansion, -but pad with leading zeros if it's less than ten characters, and if -it's more than ten characters, shorten by removing characters from the -left.'' - - @node Checksum/Hash @section Checksum/Hash @cindex MD5 checksum diff --git a/lisp/format-spec.el b/lisp/format-spec.el index f418cea425..4bf636e685 100644 --- a/lisp/format-spec.el +++ b/lisp/format-spec.el @@ -29,35 +29,46 @@ (defun format-spec (format specification &optional only-present) "Return a string based on FORMAT and SPECIFICATION. -FORMAT is a string containing `format'-like specs like \"su - %u %k\", -while SPECIFICATION is an alist mapping from format spec characters -to values. +FORMAT is a string containing `format'-like specs like \"su - %u %k\". +SPECIFICATION is an alist mapping format specification characters +to their substitutions. For instance: (format-spec \"su - %u %l\" - `((?u . ,(user-login-name)) + \\=`((?u . ,(user-login-name)) (?l . \"ls\"))) -Each format spec can have modifiers, where \"%<010b\" means \"if -the expansion is shorter than ten characters, zero-pad it, and if -it's longer, chop off characters from the left side\". +Each %-spec may contain optional flag and width modifiers, as +follows: -The following modifiers are allowed: + %character -* 0: Use zero-padding. -* -: Pad to the right. -* ^: Upper-case the expansion. -* _: Lower-case the expansion. -* <: Limit the length by removing chars from the left. -* >: Limit the length by removing chars from the right. +The following flags are allowed: -Any text properties on a %-spec itself are propagated to the text -that it generates. +* 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, by removing leading characters. +* >: Truncate to the width, if given, by removing trailing characters. +* ^: Convert to upper case. +* _: Convert to lower case. -If ONLY-PRESENT, format spec characters not present in -SPECIFICATION are ignored, and the \"%\" characters are left -where they are, including \"%%\" strings." +The width modifier behaves like the corresponding one 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 +leading zeros or truncating leading characters until it's ten +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 +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." (with-temp-buffer (insert format) (goto-char (point-min)) -- 2.26.2 --=-=-= Content-Type: text/plain Eli Zaretskii writes: >> From: "Basil L. Contovounesios" >> Cc: 41571@debbugs.gnu.org >> Date: Thu, 28 May 2020 11:41:54 +0100 >> >> While I'm at it, may I change the node name? > > Sure, the names aren't cast in stone, and the current one doesn't > strike me as especially successful/accurate. > >> In fact, couldn't format-spec be documented alongside format and >> format-message under "(elisp) Formatting Strings"? > > I thought about that as well when I read your original message, but > concluded that "Formatting Strings" is already too long. We could > end that node with a sentence referring to the next one, so that > interested readers could continue there right away. WDYT? SGTM. >> Just to recap: format-spec is like format, except it allows custom >> %-sequence characters, such as %z, which are substituted in a similar >> way to format's %s. A common use case is to allow users to customise >> different output presented to them via custom format control strings. > > This text is sorely missed at the beginning of the node about > format-spec. How's the attached for emacs-27? Thanks, -- Basil --=-=-=--