substitute-command-keys now curves quotes

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.
This commit is contained in:
Paul Eggert 2015-05-28 00:06:13 -07:00
parent 2363d498fb
commit 11b2744f48
3 changed files with 54 additions and 13 deletions

View file

@ -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\
\\<minibuffer-local-must-match-map>\\[abort-recursive-edit].")
@result{} "To abort a recursive edit from the minibuffer, type C-g."
`\\<minibuffer-local-must-match-map>\\[abort-recursive-edit]'.")
@result{} "To abort a recursive edit from the minibuffer, type C-g."
@end group
@end smallexample

View file

@ -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.

View file

@ -693,15 +693,21 @@ summary).
Each substring of the form \\=\\<MAPVAR> 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