From 11b2744f48fc03f1511de1152ad49807557c6f85 Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Thu, 28 May 2015 00:06:13 -0700 Subject: [PATCH] substitute-command-keys now curves quotes MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit So, for example, it turns "`abc'" into "‘abc’" (Bug#20385). * doc/lispref/help.texi (Keys in Documentation): * etc/NEWS: Document this. * src/doc.c (Fsubstitute_command_keys): Implement it. --- doc/lispref/help.texi | 22 +++++++++++++++------- etc/NEWS | 6 ++++++ src/doc.c | 39 +++++++++++++++++++++++++++++++++------ 3 files changed, 54 insertions(+), 13 deletions(-) diff --git a/doc/lispref/help.texi b/doc/lispref/help.texi index 868d2843569..ce29f3f5bc3 100644 --- a/doc/lispref/help.texi +++ b/doc/lispref/help.texi @@ -318,10 +318,18 @@ stands for no text itself. It is used only for a side effect: it specifies @var{mapvar}'s value as the keymap for any following @samp{\[@var{command}]} sequences in this documentation string. +@item ` +(grave accent) stands for a left single quotation mark (@samp{‘}). + +@item ' +(apostrophe) stands for a right single quotation mark (@samp{’}) if +preceded by grave accent and there are no intervening apostrophes. +Otherwise, apostrophe stands for itself. + @item \= -quotes the following character and is discarded; thus, @samp{\=\[} puts -@samp{\[} into the output, and @samp{\=\=} puts @samp{\=} into the -output. +quotes the following character and is discarded; thus, @samp{\=`} puts +@samp{`} into the output, @samp{\=\[} puts @samp{\[} into the output, +and @samp{\=\=} puts @samp{\=} into the output. @end table @strong{Please note:} Each @samp{\} must be doubled when written in a @@ -354,8 +362,8 @@ specifies a key binding that the command does not actually have. @smallexample @group (substitute-command-keys - "To abort recursive edit, type: \\[abort-recursive-edit]") -@result{} "To abort recursive edit, type: C-]" + "To abort recursive edit, type ‘\\[abort-recursive-edit]’.") +@result{} "To abort recursive edit, type ‘C-]’." @end group @group @@ -376,8 +384,8 @@ C-g abort-recursive-edit @group (substitute-command-keys "To abort a recursive edit from the minibuffer, type\ -\\\\[abort-recursive-edit].") -@result{} "To abort a recursive edit from the minibuffer, type C-g." +`\\\\[abort-recursive-edit]'.") +@result{} "To abort a recursive edit from the minibuffer, type ‘C-g’." @end group @end smallexample diff --git a/etc/NEWS b/etc/NEWS index a220330ebbf..5afd40e6727 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -805,6 +805,12 @@ when signaling a file error. For example, it now reports "Permission denied" instead of "permission denied". The old behavior was problematic in languages like German where downcasing rules depend on grammar. +** (substitute-command-keys "`foo'") now returns "‘foo’". +That is, it replaces grave accents by left single quotation marks, and +apostrophes that match grave accents by right single quotation marks. +As before, isolated apostrophes and characters preceded by \= are +output as-is. + +++ ** The character classes [:alpha:] and [:alnum:] in regular expressions now match multibyte characters using Unicode character properties. diff --git a/src/doc.c b/src/doc.c index 8b18fb0a5a2..32d65563fd9 100644 --- a/src/doc.c +++ b/src/doc.c @@ -693,15 +693,21 @@ summary). Each substring of the form \\=\\ specifies the use of MAPVAR as the keymap for future \\=\\[COMMAND] substrings. -\\=\\= quotes the following character and is discarded; -thus, \\=\\=\\=\\= puts \\=\\= into the output, and \\=\\=\\=\\[ puts \\=\\[ into the output. + +Each \\=` is replaced by ‘. Each ' preceded by \\=` and without +intervening ' is replaced by ’. + +\\=\\= quotes the following character and is discarded; thus, +\\=\\=\\=\\= puts \\=\\= into the output, \\=\\=\\=\\[ puts \\=\\[ into the output, and +\\=\\=\\=` puts \\=` into the output. Return the original STRING if no substitutions are made. Otherwise, return a new string. */) (Lisp_Object string) { char *buf; - bool changed = 0; + bool changed = false; + bool in_quote = false; unsigned char *strp; char *bufp; ptrdiff_t idx; @@ -734,6 +740,12 @@ Otherwise, return a new string. */) keymap = Voverriding_local_map; bsize = SBYTES (string); + + /* Add some room for expansion due to quote replacement. */ + enum { EXTRA_ROOM = 20 }; + if (bsize <= STRING_BYTES_BOUND - EXTRA_ROOM) + bsize += EXTRA_ROOM; + bufp = buf = xmalloc (bsize); strp = SDATA (string); @@ -743,7 +755,7 @@ Otherwise, return a new string. */) { /* \= quotes the next character; thus, to put in \[ without its special meaning, use \=\[. */ - changed = 1; + changed = true; strp += 2; if (multibyte) { @@ -766,7 +778,6 @@ Otherwise, return a new string. */) ptrdiff_t start_idx; bool follow_remap = 1; - changed = 1; strp += 2; /* skip \[ */ start = strp; start_idx = start - SDATA (string); @@ -833,7 +844,6 @@ Otherwise, return a new string. */) Lisp_Object earlier_maps; ptrdiff_t count = SPECPDL_INDEX (); - changed = 1; strp += 2; /* skip \{ or \< */ start = strp; start_idx = start - SDATA (string); @@ -903,6 +913,7 @@ Otherwise, return a new string. */) length = SCHARS (tem); length_byte = SBYTES (tem); subst: + changed = true; { ptrdiff_t offset = bufp - buf; if (STRING_BYTES_BOUND - length_byte < bsize) @@ -916,6 +927,22 @@ Otherwise, return a new string. */) strp = SDATA (string) + idx; } } + else if (strp[0] == '`') + { + in_quote = true; + start = (unsigned char *) "\xE2\x80\x98"; /* ‘ */ + subst_quote: + length = 1; + length_byte = 3; + idx = strp - SDATA (string) + 1; + goto subst; + } + else if (strp[0] == '\'' && in_quote) + { + in_quote = false; + start = (unsigned char *) "\xE2\x80\x99"; /* ’ */ + goto subst_quote; + } else if (! multibyte) /* just copy other chars */ *bufp++ = *strp++, nchars++; else -- 2.39.2