From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!not-for-mail From: Paul Eggert Newsgroups: gmane.emacs.bugs Subject: bug#21222: [PROPOSED PATCH] Extend =?UTF-8?Q?=E2=80=98format=E2=80=99?= to translate curved quotes Date: Sat, 8 Aug 2015 21:41:34 -0700 Message-ID: <1439095294-21757-1-git-send-email-eggert@cs.ucla.edu> NNTP-Posting-Host: plane.gmane.org Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable X-Trace: ger.gmane.org 1439095414 9752 80.91.229.3 (9 Aug 2015 04:43:34 GMT) X-Complaints-To: usenet@ger.gmane.org NNTP-Posting-Date: Sun, 9 Aug 2015 04:43:34 +0000 (UTC) Cc: Paul Eggert To: 21222@debbugs.gnu.org Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane.org@gnu.org Sun Aug 09 06:43:22 2015 Return-path: Envelope-to: geb-bug-gnu-emacs@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by plane.gmane.org with esmtp (Exim 4.69) (envelope-from ) id 1ZOIS7-0001dD-DD for geb-bug-gnu-emacs@m.gmane.org; Sun, 09 Aug 2015 06:43:19 +0200 Original-Received: from localhost ([::1]:54369 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZOIS6-0005Xp-Dp for geb-bug-gnu-emacs@m.gmane.org; Sun, 09 Aug 2015 00:43:18 -0400 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:45760) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZOIRz-0005W5-8k for bug-gnu-emacs@gnu.org; Sun, 09 Aug 2015 00:43:14 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ZOIRr-00052Z-5z for bug-gnu-emacs@gnu.org; Sun, 09 Aug 2015 00:43:11 -0400 Original-Received: from debbugs.gnu.org ([140.186.70.43]:46673) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZOIRr-00052V-1Z for bug-gnu-emacs@gnu.org; Sun, 09 Aug 2015 00:43:03 -0400 Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.80) (envelope-from ) id 1ZOIRq-0006pu-K7 for bug-gnu-emacs@gnu.org; Sun, 09 Aug 2015 00:43:02 -0400 X-Loop: help-debbugs@gnu.org Resent-From: Paul Eggert Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Sun, 09 Aug 2015 04:43:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: report 21222 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.143909532426210 (code B ref -1); Sun, 09 Aug 2015 04:43:02 +0000 Original-Received: (at submit) by debbugs.gnu.org; 9 Aug 2015 04:42:04 +0000 Original-Received: from localhost ([127.0.0.1]:50889 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.80) (envelope-from ) id 1ZOIQs-0006oe-JV for submit@debbugs.gnu.org; Sun, 09 Aug 2015 00:42:04 -0400 Original-Received: from eggs.gnu.org ([208.118.235.92]:60372) by debbugs.gnu.org with esmtp (Exim 4.80) (envelope-from ) id 1ZOIQo-0006oC-8L for submit@debbugs.gnu.org; Sun, 09 Aug 2015 00:42:00 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ZOIQk-0004xo-9s for submit@debbugs.gnu.org; Sun, 09 Aug 2015 00:41:57 -0400 Original-Received: from lists.gnu.org ([2001:4830:134:3::11]:43678) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZOIQk-0004xk-6h for submit@debbugs.gnu.org; Sun, 09 Aug 2015 00:41:54 -0400 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:45641) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZOIQg-0005Gy-OC for bug-gnu-emacs@gnu.org; Sun, 09 Aug 2015 00:41:54 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ZOIQd-0004xK-D5 for bug-gnu-emacs@gnu.org; Sun, 09 Aug 2015 00:41:50 -0400 Original-Received: from zimbra.cs.ucla.edu ([131.179.128.68]:40381) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZOIQd-0004xE-0r for bug-gnu-emacs@gnu.org; Sun, 09 Aug 2015 00:41:47 -0400 Original-Received: from localhost (localhost [127.0.0.1]) by zimbra.cs.ucla.edu (Postfix) with ESMTP id 487C8160D0A for ; Sat, 8 Aug 2015 21:41:45 -0700 (PDT) Original-Received: from zimbra.cs.ucla.edu ([127.0.0.1]) by localhost (zimbra.cs.ucla.edu [127.0.0.1]) (amavisd-new, port 10032) with ESMTP id U794XsnNAfhF; Sat, 8 Aug 2015 21:41:43 -0700 (PDT) Original-Received: from localhost (localhost [127.0.0.1]) by zimbra.cs.ucla.edu (Postfix) with ESMTP id F04E2160D09; Sat, 8 Aug 2015 21:41:42 -0700 (PDT) X-Virus-Scanned: amavisd-new at zimbra.cs.ucla.edu Original-Received: from zimbra.cs.ucla.edu ([127.0.0.1]) by localhost (zimbra.cs.ucla.edu [127.0.0.1]) (amavisd-new, port 10026) with ESMTP id FXfgvWNZQk_1; Sat, 8 Aug 2015 21:41:42 -0700 (PDT) Original-Received: from Penguin.CS.UCLA.EDU (Penguin.CS.UCLA.EDU [131.179.64.200]) by zimbra.cs.ucla.edu (Postfix) with ESMTPSA id CED861601D0; Sat, 8 Aug 2015 21:41:42 -0700 (PDT) X-Mailer: git-send-email 2.1.0 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x X-detected-operating-system: by eggs.gnu.org: Error: Malformed IPv6 address (bad octet value). X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.15 Precedence: list X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x X-Received-From: 140.186.70.43 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.org@gnu.org Original-Sender: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane.org@gnu.org Xref: news.gmane.org gmane.emacs.bugs:105372 Archived-At: This is a followup to the recent doc string change, and deals with diagnostics and the like. This patch is more conservative than the doc string change, in that the behavior of =E2=80=98format=E2=80=99 c= hanges only if its first arg contains curved quotes and the user prefers straight or grave quotes. (Come to think of it, perhaps we should be similarly conservative with doc strings too, but that can wait.) The upside of this conservatism is that existing usage is almost surely unaffected. The downside is that we'll eventually have to change Emacs's format strings to use curved quotes in places where the user might want curved curved quotes, but that's a simple and mechanical translation that I'm willing to do later. * doc/lispref/help.texi (Keys in Documentation): Move description of text-quoting-style from here ... * doc/lispref/strings.texi (Formatting Strings): ... to here, and describe new behavior of =E2=80=98format=E2=80=99. * etc/NEWS: Describe new behavior. * lisp/calc/calc-help.el (calc-describe-thing): * lisp/emacs-lisp/derived.el (derived-mode-make-docstring): * lisp/info.el (Info-find-index-name): Use =E2=80=98concat=E2=80=99 rather than =E2=80=98format=E2=80=99 to avoi= d misinterpretation of recently-added curved quotes. * src/doc.c (uLSQM0, uLSQM1, uLSQM2, uRSQM0, uRSQM1, uRSQM2): Move from here ... * src/lisp.h: ... to here. * src/doc.c (text_quoting_style): New function. (Fsubstitute_command_keys): Use it. * src/editfns.c (Fformat): Implement new behavior. * src/lisp.h (enum text_quoting_style): New enum. --- doc/lispref/help.texi | 12 +-- doc/lispref/strings.texi | 73 +++++++++++----- etc/NEWS | 25 ++++-- lisp/calc/calc-help.el | 2 +- lisp/emacs-lisp/derived.el | 5 +- lisp/info.el | 6 +- src/doc.c | 57 +++++++------ src/editfns.c | 202 ++++++++++++++++++++++++++-------------= ------ src/lisp.h | 19 +++++ 9 files changed, 244 insertions(+), 157 deletions(-) diff --git a/doc/lispref/help.texi b/doc/lispref/help.texi index ca8ae3f..ab1696e 100644 --- a/doc/lispref/help.texi +++ b/doc/lispref/help.texi @@ -347,19 +347,11 @@ and @samp{\=3D\=3D} puts @samp{\=3D} into the outpu= t. @strong{Please note:} Each @samp{\} must be doubled when written in a string in Emacs Lisp. =20 -@defvar text-quoting-style @cindex curved quotes @cindex curly quotes -The value of this variable specifies the style +The value of the @code{text-quoting-style} variable specifies the style @code{substitute-command-keys} uses when generating left and right -quotes. If the variable's value is @code{curve}, the style is -@t{=E2=80=98like this=E2=80=99} with curved single quotes. If the value= is -@code{straight}, the style is @t{'like this'} with straight -apostrophes. If the value is @code{grave}, the style is @t{`like -this'} with grave accent and apostrophe. The default value @code{nil} -acts like @code{curve} if curved single quotes are displayable, and -like @code{grave} otherwise. -@end defvar +quotes. @xref{Formatting Strings}, for more information. =20 @defun substitute-command-keys string This function scans @var{string} for the above special sequences and diff --git a/doc/lispref/strings.texi b/doc/lispref/strings.texi index c2f0607..3093338 100644 --- a/doc/lispref/strings.texi +++ b/doc/lispref/strings.texi @@ -805,22 +805,27 @@ formatting feature described here; they differ from= @code{format} only in how they use the result of formatting. =20 @defun format string &rest objects -This function returns a new string that is made by copying +This function returns a string that is equivalent to copying @var{string} and then replacing any format specification in the copy with encodings of the corresponding @var{objects}. The arguments @var{objects} are the computed values to be formatted. =20 The characters in @var{string}, other than the format specifications, are copied directly into the output, including their text properties, -if any. +if any. If the output equals @var{string}, this function may return +@var{string} itself rather than a new copy. @end defun =20 @cindex @samp{%} in format @cindex format specification +@cindex curved quotes +@cindex curly quotes A format specification is a sequence of characters beginning with a -@samp{%}. Thus, if there is a @samp{%d} in @var{string}, the -@code{format} function replaces it with the printed representation of -one of the values to be formatted (one of the arguments @var{objects}). +@samp{%} or is a curved single quotation mark. Except for @samp{%%} +and quotation marks, each format specification says how to represent +one of the arguments @var{objects}. For example, if there +is a @samp{%d} in @var{string}, the @code{format} function replaces it +with the decimal representation of the integer to be formatted. For example: =20 @example @@ -830,11 +835,12 @@ For example: @end group @end example =20 - Since @code{format} interprets @samp{%} characters as format + Since @code{format} interprets @samp{%}, @samp{=E2=80=98} and @samp{=E2= =80=99} +characters as format specifications, you should @emph{never} pass an arbitrary string as the first argument. This is particularly true when the string is generated by some Lisp code. Unless the string is @emph{known} to -never include any @samp{%} characters, pass @code{"%s"}, described +never include any of the three special characters, pass @code{"%s"}, des= cribed below, as the first argument, and the string as the second, like this: =20 @example @@ -908,20 +914,30 @@ is shorter. Replace the specification with a single @samp{%}. This format specification is unusual in that it does not use a value. For example, @code{(format "%% %d" 30)} returns @code{"% 30"}. + +@item =E2=80=98 +@itemx =E2=80=99 +@cindex curved quotes +@cindex curly quotes +Replace the specification with a left or right quote, respectively. +Although typically a curved single quotation mark stands for itself, +other quoting styles are available as per the variable +@samp{text-quoting-style} described below. @end table =20 - Any other format character results in an @samp{Invalid format + Any other format character after @samp{%} results in an @samp{Invalid = format operation} error. =20 - Here are several examples: + Here are several examples, which assume the typical quoting style +where curved single quotes stand for themselves: =20 @example @group -(format "The name of this buffer is %s." (buffer-name)) - @result{} "The name of this buffer is strings.texi." +(format "The name of this buffer is =E2=80=98%s=E2=80=99." (buffer-name)= ) + @result{} "The name of this buffer is =E2=80=98strings.texi=E2=80=99= ." =20 -(format "The buffer object prints as %s." (current-buffer)) - @result{} "The buffer object prints as strings.texi." +(format "The buffer object prints as =E2=80=98%s=E2=80=99." (current-buf= fer)) + @result{} "The buffer object prints as =E2=80=98strings.texi=E2=80=99= ." =20 (format "The octal value of %d is %o, and the hex value is %x." 18 18 18) @@ -932,7 +948,7 @@ operation} error. =20 @cindex field width @cindex padding - A specification can have a @dfn{width}, which is a decimal number + A @samp{%} specification can have a @dfn{width}, which is a decimal nu= mber between the @samp{%} and the specification character. If the printed representation of the object contains fewer characters than this width, @code{format} extends it with padding. The width specifier is @@ -948,7 +964,7 @@ the width specifier normally consists of spaces inser= ted on the left: If the width is too small, @code{format} does not truncate the object's printed representation. Thus, you can use a width to specify a minimum spacing between columns with no risk of losing information. -In the following three examples, @samp{%7s} specifies a minimum width +In the following two examples, @samp{%7s} specifies a minimum width of 7. In the first case, the string inserted in place of @samp{%7s} has only 3 letters, and needs 4 blank spaces as padding. In the second case, the string @code{"specification"} is 13 letters wide but @@ -956,12 +972,12 @@ is not truncated. =20 @example @group -(format "The word '%7s' has %d letters in it." +(format "The word =E2=80=98%7s=E2=80=99 has %d letters in it." "foo" (length "foo")) - @result{} "The word ' foo' has 3 letters in it." -(format "The word '%7s' has %d letters in it." + @result{} "The word =E2=80=98 foo=E2=80=99 has 3 letters in it." +(format "The word =E2=80=98%7s=E2=80=99 has %d letters in it." "specification" (length "specification")) - @result{} "The word 'specification' has 13 letters in it." + @result{} "The word =E2=80=98specification=E2=80=99 has 13 letters = in it." @end group @end example =20 @@ -1003,14 +1019,14 @@ ignored. (format "%-6d is padded on the right" 123) @result{} "123 is padded on the right" =20 -(format "The word '%-7s' actually has %d letters in it." +(format "The word =E2=80=98%-7s=E2=80=99 actually has %d letters in it." "foo" (length "foo")) - @result{} "The word 'foo ' actually has 3 letters in it." + @result{} "The word =E2=80=98foo =E2=80=99 actually has 3 letter= s in it." @end group @end example =20 @cindex precision in format specifications - All the specification characters allow an optional @dfn{precision} + The @samp{%} specification characters allow an optional @dfn{precision= } before the character (after the width, if present). The precision is a decimal-point @samp{.} followed by a digit-string. For the floating-point specifications (@samp{%e}, @samp{%f}, @samp{%g}), the @@ -1021,6 +1037,19 @@ shows only the first three characters of the repre= sentation for @var{object}. Precision has no effect for other specification characters. =20 +@defvar text-quoting-style +@cindex curved quotes +@cindex curly quotes +This variable specifies the style @code{format} uses when generating +left and right quotes. If the value is @code{curve}, the style is +@t{=E2=80=98like this=E2=80=99} with curved single quotes. If the value= is +@code{straight}, the style is @t{'like this'} with straight +apostrophes. If the value is @code{grave}, the style is @t{`like +this'} with grave accent and apostrophe. The default value @code{nil} +acts like @code{curve} if curved single quotes are displayable, and +like @code{grave} otherwise. +@end defvar + @node Case Conversion @section Case Conversion in Lisp @cindex upper case diff --git a/etc/NEWS b/etc/NEWS index 0a33a6e..9723dec 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -892,16 +892,25 @@ denied" instead of "permission denied". The old be= havior was problematic in languages like German where downcasing rules depend on grammar. =20 +++ +** =E2=80=98format=E2=80=99 now replaces curved single quotes. +That is, it replaces strings' curved single quotes (also known as +curly quotes) as per the value of the new custom variable +=E2=80=98text-quoting-style=E2=80=99: =E2=80=98curve=E2=80=99 means repl= ace curved quotes with +themselves =E2=80=98like this=E2=80=99, =E2=80=98straight=E2=80=99 means= use straight apostrophes +'like this', =E2=80=98grave=E2=80=99 means use grave accent and apostrop= he `like +this', and nil (default) means use curved quotes if displayable and +grave accent and apostrophe otherwise. Because it now may be used +in many contexts where it's a no-op, =E2=80=98format=E2=80=99 is no long= er required to +create a string, and may return its first argument if the argument +already has the correct value. + ++++ ** substitute-command-keys now replaces quotes. That is, it converts documentation strings' quoting style as per the -value of the new custom variable =E2=80=98text-quoting-style=E2=80=99: =E2= =80=98curve=E2=80=99 means -use curved quotes (also known as curly quotes) =E2=80=98like this=E2=80=99= , =E2=80=98straight=E2=80=99 -means use straight apostrophes 'like this', =E2=80=98grave=E2=80=99 mean= s use grave -accent and apostrophe `like this', and nil (default) means use curved -quotes if displayable and grave accent and apostrophe otherwise. Doc -strings in source code can use either curved quotes or grave accent -and apostrophe. As before, isolated apostrophes and characters -preceded by \=3D are output as-is. +value of =E2=80=98text-quoting-style=E2=80=99 as described above. Doc s= trings in +source code can use either curved quotes or grave accent and +apostrophe. As before, isolated apostrophes and characters preceded +by \=3D are output as-is. =20 +++ ** The character classes [:alpha:] and [:alnum:] in regular expressions diff --git a/lisp/calc/calc-help.el b/lisp/calc/calc-help.el index b2e7df1..7a1a983 100644 --- a/lisp/calc/calc-help.el +++ b/lisp/calc/calc-help.el @@ -365,7 +365,7 @@ C-w Describe how there is no warranty for Calc." (let (Info-history) (Info-goto-node (buffer-substring (match-beginning 1) (match-end 1= )))) (let* ((string-target (or target thing)) - (quoted (format "['`=E2=80=98]%s['=E2=80=99]" (regexp-quote s= tring-target))) + (quoted (concat "['`=E2=80=98]" (regexp-quote string-target) = "['=E2=80=99]")) (bracketed (format "\\[%s\\]\\|(%s)\\|\\" makeinfo can append diff --git a/src/doc.c b/src/doc.c index 36619e1..9864b46 100644 --- a/src/doc.c +++ b/src/doc.c @@ -684,18 +684,34 @@ the same file name is found in the `doc-directory'.= */) return unbind_to (count, Qnil); } =0C -/* Declare named constants for U+2018 LEFT SINGLE QUOTATION MARK and - U+2019 RIGHT SINGLE QUOTATION MARK, which have UTF-8 encodings - "\xE2\x80\x98" and "\xE2\x80\x99", respectively. */ -enum - { - LEFT_SINGLE_QUOTATION_MARK =3D 0x2018, - uLSQM0 =3D 0xE2, uLSQM1 =3D 0x80, uLSQM2 =3D 0x98, - uRSQM0 =3D 0xE2, uRSQM1 =3D 0x80, uRSQM2 =3D 0x99, - }; +/* U+2018 LEFT SINGLE QUOTATION MARK as an integer and as a sequence + of bytes. */ +enum { LEFT_SINGLE_QUOTATION_MARK =3D 0x2018 }; static unsigned char const LSQM[] =3D { uLSQM0, uLSQM1, uLSQM2 }; + +/* U+2019 RIGHT SINGLE QUOTATION MARK as a sequence of bytes. */ static unsigned char const RSQM[] =3D { uRSQM0, uRSQM1, uRSQM2 }; =20 +/* Return the current effective text quoting style. */ +enum text_quoting_style +text_quoting_style (void) +{ + if (EQ (Vtext_quoting_style, Qgrave)) + return GRAVE_QUOTING_STYLE; + else if (EQ (Vtext_quoting_style, Qstraight)) + return STRAIGHT_QUOTING_STYLE; + else if (NILP (Vtext_quoting_style) + && DISP_TABLE_P (Vstandard_display_table)) + { + Lisp_Object dv =3D DISP_CHAR_VECTOR (XCHAR_TABLE (Vstandard_displa= y_table), + LEFT_SINGLE_QUOTATION_MARK); + if (VECTORP (dv) && ASIZE (dv) =3D=3D 1 + && EQ (AREF (dv, 0), make_number ('`'))) + return GRAVE_QUOTING_STYLE; + } + return CURVE_QUOTING_STYLE; +} + DEFUN ("substitute-command-keys", Fsubstitute_command_keys, Ssubstitute_command_keys, 1, 1, 0, doc: /* Substitute key descriptions for command names in STRING. @@ -750,20 +766,7 @@ Otherwise, return a new string. */) name =3D Qnil; GCPRO4 (string, tem, keymap, name); =20 - enum { unicode, grave_accent, apostrophe } quote_translation =3D unico= de; - if (EQ (Vtext_quoting_style, Qgrave)) - quote_translation =3D grave_accent; - else if (EQ (Vtext_quoting_style, Qstraight)) - quote_translation =3D apostrophe; - else if (NILP (Vtext_quoting_style) - && DISP_TABLE_P (Vstandard_display_table)) - { - Lisp_Object dv =3D DISP_CHAR_VECTOR (XCHAR_TABLE (Vstandard_displa= y_table), - LEFT_SINGLE_QUOTATION_MARK); - if (VECTORP (dv) && ASIZE (dv) =3D=3D 1 - && EQ (AREF (dv, 0), make_number ('`'))) - quote_translation =3D grave_accent; - } + enum text_quoting_style quoting_style =3D text_quoting_style (); =20 multibyte =3D STRING_MULTIBYTE (string); nchars =3D 0; @@ -962,7 +965,7 @@ Otherwise, return a new string. */) strp =3D SDATA (string) + idx; } } - else if (strp[0] =3D=3D '`' && quote_translation =3D=3D unicode) + else if (strp[0] =3D=3D '`' && quoting_style =3D=3D CURVE_QUOTING_= STYLE) { in_quote =3D true; start =3D LSQM; @@ -972,7 +975,7 @@ Otherwise, return a new string. */) idx =3D strp - SDATA (string) + 1; goto subst; } - else if (strp[0] =3D=3D '`' && quote_translation =3D=3D apostrophe= ) + else if (strp[0] =3D=3D '`' && quoting_style =3D=3D STRAIGHT_QUOTI= NG_STYLE) { *bufp++ =3D '\''; strp++; @@ -987,9 +990,9 @@ Otherwise, return a new string. */) } else if (strp[0] =3D=3D uLSQM0 && strp[1] =3D=3D uLSQM1 && (strp[2] =3D=3D uLSQM2 || strp[2] =3D=3D uRSQM2) - && quote_translation !=3D unicode) + && quoting_style !=3D CURVE_QUOTING_STYLE) { - *bufp++ =3D (strp[2] =3D=3D uLSQM2 && quote_translation =3D=3D grave_= accent + *bufp++ =3D (strp[2] =3D=3D uLSQM2 && quoting_style =3D=3D GRAVE_QUOT= ING_STYLE ? '`' : '\''); strp +=3D 3; nchars++; diff --git a/src/editfns.c b/src/editfns.c index 9ff39f9..ed57d8a 100644 --- a/src/editfns.c +++ b/src/editfns.c @@ -3800,8 +3800,9 @@ DEFUN ("format", Fformat, Sformat, 1, MANY, 0, The first argument is a format control string. The other arguments are substituted into it to make the result, a string= . =20 -The format control string may contain %-sequences meaning to substitute -the next available argument: +The format control string may contain ordinary characters, +%-sequences meaning to substitute the next available argument, +and curved single quotation marks meaning to substitute quotes. =20 %s means print a string argument. Actually, prints any object, with `pr= inc'. %d means print as number in decimal (%o octal, %x hex). @@ -3846,6 +3847,12 @@ precision specifier says how many decimal places t= o show; if zero, the decimal point itself is omitted. For %s and %S, the precision specifier truncates the string to the given width. =20 +\\=3D=E2=80=98 and \\=3D=E2=80=99 means print left and right quotes as p= er +=E2=80=98text-quoting-style=E2=80=99. + +Return the first argument if it contains no format directives. +Otherwise, return a new string. + usage: (format STRING &rest OBJECTS) */) (ptrdiff_t nargs, Lisp_Object *args) { @@ -3858,6 +3865,7 @@ usage: (format STRING &rest OBJECTS) */) ptrdiff_t buf_save_value_index IF_LINT (=3D 0); char *format, *end, *format_start; ptrdiff_t formatlen, nchars; + bool changed =3D false; /* True if the format is multibyte. */ bool multibyte_format =3D 0; /* True if the output should be a multibyte string, @@ -3921,6 +3929,8 @@ usage: (format STRING &rest OBJECTS) */) if (STRINGP (args[n]) && STRING_MULTIBYTE (args[n])) multibyte =3D 1; =20 + enum text_quoting_style quoting_style =3D text_quoting_style (); + /* If we start out planning a unibyte result, then discover it has to be multibyte, we jump back to retry. */ retry: @@ -4005,6 +4015,7 @@ usage: (format STRING &rest OBJECTS) */) if (format =3D=3D end) error ("Format string ends in middle of format specifier"); =20 + changed =3D true; memset (&discarded[format0 - format_start], 1, format - format0); conversion =3D *format; if (conversion =3D=3D '%') @@ -4426,6 +4437,20 @@ usage: (format STRING &rest OBJECTS) */) =20 convbytes =3D format - src; memset (&discarded[src + 1 - format_start], 2, convbytes - 1); + + if (quoting_style !=3D CURVE_QUOTING_STYLE && convbytes =3D=3D 3 + && (unsigned char) src[0] =3D=3D uLSQM0 + && (unsigned char) src[1] =3D=3D uLSQM1 + && ((unsigned char) src[2] =3D=3D uLSQM2 + || (unsigned char) src[2] =3D=3D uRSQM2)) + { + convbytes =3D 1; + str[0] =3D (((unsigned char) src[2] =3D=3D uLSQM2 + && quoting_style =3D=3D GRAVE_QUOTING_STYLE) + ? '`' : '\''); + src =3D (char *) str; + changed =3D true; + } } else { @@ -4437,6 +4462,7 @@ usage: (format STRING &rest OBJECTS) */) int c =3D BYTE8_TO_CHAR (uc); convbytes =3D CHAR_STRING (c, str); src =3D (char *) str; + changed =3D true; } } =20 @@ -4484,113 +4510,119 @@ usage: (format STRING &rest OBJECTS) */) if (bufsize < p - buf) emacs_abort (); =20 - if (maybe_combine_byte) - nchars =3D multibyte_chars_in_text ((unsigned char *) buf, p - buf); - val =3D make_specified_string (buf, nchars, p - buf, multibyte); - - /* If the format string has text properties, or any of the string - arguments has text properties, set up text properties of the - result string. */ - - if (string_intervals (args[0]) || arg_intervals) + if (!changed) + val =3D args[0]; + else { - Lisp_Object len, new_len, props; - struct gcpro gcpro1; + if (maybe_combine_byte) + nchars =3D multibyte_chars_in_text ((unsigned char *) buf, p - buf); + val =3D make_specified_string (buf, nchars, p - buf, multibyte); =20 - /* Add text properties from the format string. */ - len =3D make_number (SCHARS (args[0])); - props =3D text_property_list (args[0], make_number (0), len, Qnil)= ; - GCPRO1 (props); + /* If the format string has text properties, or any of the string + arguments has text properties, set up text properties of the + result string. */ =20 - if (CONSP (props)) + if (string_intervals (args[0]) || arg_intervals) { - ptrdiff_t bytepos =3D 0, position =3D 0, translated =3D 0; - ptrdiff_t argn =3D 1; - Lisp_Object list; - - /* Adjust the bounds of each text property - to the proper start and end in the output string. */ - - /* Put the positions in PROPS in increasing order, so that - we can do (effectively) one scan through the position - space of the format string. */ - props =3D Fnreverse (props); - - /* BYTEPOS is the byte position in the format string, - POSITION is the untranslated char position in it, - TRANSLATED is the translated char position in BUF, - and ARGN is the number of the next arg we will come to. */ - for (list =3D props; CONSP (list); list =3D XCDR (list)) + Lisp_Object len, new_len, props; + struct gcpro gcpro1; + + /* Add text properties from the format string. */ + len =3D make_number (SCHARS (args[0])); + props =3D text_property_list (args[0], make_number (0), len, Qnil); + GCPRO1 (props); + + if (CONSP (props)) { - Lisp_Object item; - ptrdiff_t pos; + ptrdiff_t bytepos =3D 0, position =3D 0, translated =3D 0; + ptrdiff_t argn =3D 1; + Lisp_Object list; + + /* Adjust the bounds of each text property + to the proper start and end in the output string. */ + + /* Put the positions in PROPS in increasing order, so that + we can do (effectively) one scan through the position + space of the format string. */ + props =3D Fnreverse (props); + + /* BYTEPOS is the byte position in the format string, + POSITION is the untranslated char position in it, + TRANSLATED is the translated char position in BUF, + and ARGN is the number of the next arg we will come to. */ + for (list =3D props; CONSP (list); list =3D XCDR (list)) + { + Lisp_Object item; + ptrdiff_t pos; =20 - item =3D XCAR (list); + item =3D XCAR (list); =20 - /* First adjust the property start position. */ - pos =3D XINT (XCAR (item)); + /* First adjust the property start position. */ + pos =3D XINT (XCAR (item)); =20 - /* Advance BYTEPOS, POSITION, TRANSLATED and ARGN - up to this position. */ - for (; position < pos; bytepos++) - { - if (! discarded[bytepos]) - position++, translated++; - else if (discarded[bytepos] =3D=3D 1) + /* Advance BYTEPOS, POSITION, TRANSLATED and ARGN + up to this position. */ + for (; position < pos; bytepos++) { - position++; - if (translated =3D=3D info[argn].start) + if (! discarded[bytepos]) + position++, translated++; + else if (discarded[bytepos] =3D=3D 1) { - translated +=3D info[argn].end - info[argn].start; - argn++; + position++; + if (translated =3D=3D info[argn].start) + { + translated +=3D info[argn].end - info[argn].start; + argn++; + } } } - } =20 - XSETCAR (item, make_number (translated)); + XSETCAR (item, make_number (translated)); =20 - /* Likewise adjust the property end position. */ - pos =3D XINT (XCAR (XCDR (item))); + /* Likewise adjust the property end position. */ + pos =3D XINT (XCAR (XCDR (item))); =20 - for (; position < pos; bytepos++) - { - if (! discarded[bytepos]) - position++, translated++; - else if (discarded[bytepos] =3D=3D 1) + for (; position < pos; bytepos++) { - position++; - if (translated =3D=3D info[argn].start) + if (! discarded[bytepos]) + position++, translated++; + else if (discarded[bytepos] =3D=3D 1) { - translated +=3D info[argn].end - info[argn].start; - argn++; + position++; + if (translated =3D=3D info[argn].start) + { + translated +=3D info[argn].end - info[argn].start; + argn++; + } } } + + XSETCAR (XCDR (item), make_number (translated)); } =20 - XSETCAR (XCDR (item), make_number (translated)); + add_text_properties_from_list (val, props, make_number (0)); } =20 - add_text_properties_from_list (val, props, make_number (0)); - } - - /* Add text properties from arguments. */ - if (arg_intervals) - for (n =3D 1; n < nargs; ++n) - if (info[n].intervals) - { - len =3D make_number (SCHARS (args[n])); - new_len =3D make_number (info[n].end - info[n].start); - props =3D text_property_list (args[n], make_number (0), len, Qnil= ); - props =3D extend_property_ranges (props, new_len); - /* If successive arguments have properties, be sure that - the value of `composition' property be the copy. */ - if (n > 1 && info[n - 1].end) - make_composition_value_copy (props); - add_text_properties_from_list (val, props, - make_number (info[n].start)); - } + /* Add text properties from arguments. */ + if (arg_intervals) + for (n =3D 1; n < nargs; ++n) + if (info[n].intervals) + { + len =3D make_number (SCHARS (args[n])); + new_len =3D make_number (info[n].end - info[n].start); + props =3D text_property_list (args[n], make_number (0), + len, Qnil); + props =3D extend_property_ranges (props, new_len); + /* If successive arguments have properties, be sure that + the value of `composition' property be the copy. */ + if (n > 1 && info[n - 1].end) + make_composition_value_copy (props); + add_text_properties_from_list (val, props, + make_number (info[n].start)); + } =20 - UNGCPRO; + UNGCPRO; + } } =20 /* If we allocated BUF or INFO with malloc, free it too. */ diff --git a/src/lisp.h b/src/lisp.h index 02109d7..ff20489 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -4299,6 +4299,25 @@ extern void set_initial_environment (void); extern void syms_of_callproc (void); =20 /* Defined in doc.c. */ +enum + { + /* Named constants for tue UTF-8 encodings of U+2018 LEFT SINGLE + QUOTATION MARK and U+2019 RIGHT SINGLE QUOTATION MARK. */ + uLSQM0 =3D 0xE2, uLSQM1 =3D 0x80, uLSQM2 =3D 0x98, + uRSQM0 =3D 0xE2, uRSQM1 =3D 0x80, uRSQM2 =3D 0x99 + }; +enum text_quoting_style + { + /* Use curved single quotes =E2=80=98like this=E2=80=99. */ + CURVE_QUOTING_STYLE, + + /* Use grave accent and apostrophe `like this'. */ + GRAVE_QUOTING_STYLE, + + /* Use apostrophes 'like this'. */ + STRAIGHT_QUOTING_STYLE + }; +extern enum text_quoting_style text_quoting_style (void); extern Lisp_Object read_doc_string (Lisp_Object); extern Lisp_Object get_doc_string (Lisp_Object, bool, bool); extern void syms_of_doc (void); --=20 2.1.0