aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPaul Eggert2015-08-14 15:50:35 -0700
committerPaul Eggert2015-08-14 15:55:57 -0700
commit244c801689d2f7a80480d83cd7d092d4762ebe08 (patch)
tree6a07abc8b26966002de304b3546f3ccbebb2c865 /src
parent293775f10555f7da7e61ed8dbdb78d72f30f7e2d (diff)
downloademacs-244c801689d2f7a80480d83cd7d092d4762ebe08.tar.gz
emacs-244c801689d2f7a80480d83cd7d092d4762ebe08.zip
Extend ‘format’ to translate curved quotes
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 ‘format’ changes 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 quotes, but that's a simple and mechanical translation that I'm willing to do later. (Bug#21222) * 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 ‘format’. * 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 ‘concat’ rather than ‘format’ to avoid 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.
Diffstat (limited to 'src')
-rw-r--r--src/doc.c52
-rw-r--r--src/editfns.c202
-rw-r--r--src/lisp.h19
3 files changed, 162 insertions, 111 deletions
diff --git a/src/doc.c b/src/doc.c
index cb52266ef58..9f0ddbc5260 100644
--- a/src/doc.c
+++ b/src/doc.c
@@ -684,19 +684,32 @@ the same file name is found in the `doc-directory'. */)
684 return unbind_to (count, Qnil); 684 return unbind_to (count, Qnil);
685} 685}
686 686
687/* Declare named constants for U+2018 LEFT SINGLE QUOTATION MARK and 687/* Curved quotation marks. */
688 U+2019 RIGHT SINGLE QUOTATION MARK, which have UTF-8 encodings
689 "\xE2\x80\x98" and "\xE2\x80\x99", respectively. */
690enum
691 {
692 uLSQM0 = 0xE2, uLSQM1 = 0x80, uLSQM2 = 0x98,
693 uRSQM0 = 0xE2, uRSQM1 = 0x80, uRSQM2 = 0x99,
694 };
695static unsigned char const LSQM[] = { uLSQM0, uLSQM1, uLSQM2 }; 688static unsigned char const LSQM[] = { uLSQM0, uLSQM1, uLSQM2 };
696static unsigned char const RSQM[] = { uRSQM0, uRSQM1, uRSQM2 }; 689static unsigned char const RSQM[] = { uRSQM0, uRSQM1, uRSQM2 };
697#define uLSQM "\xE2\x80\x98" 690#define uLSQM "\xE2\x80\x98"
698#define uRSQM "\xE2\x80\x99" 691#define uRSQM "\xE2\x80\x99"
699 692
693/* Return the current effective text quoting style. */
694enum text_quoting_style
695text_quoting_style (void)
696{
697 if (EQ (Vtext_quoting_style, Qgrave))
698 return GRAVE_QUOTING_STYLE;
699 else if (EQ (Vtext_quoting_style, Qstraight))
700 return STRAIGHT_QUOTING_STYLE;
701 else if (NILP (Vtext_quoting_style)
702 && DISP_TABLE_P (Vstandard_display_table))
703 {
704 Lisp_Object dv = DISP_CHAR_VECTOR (XCHAR_TABLE (Vstandard_display_table),
705 LEFT_SINGLE_QUOTATION_MARK);
706 if (VECTORP (dv) && ASIZE (dv) == 1
707 && EQ (AREF (dv, 0), make_number ('`')))
708 return GRAVE_QUOTING_STYLE;
709 }
710 return CURVE_QUOTING_STYLE;
711}
712
700DEFUN ("substitute-command-keys", Fsubstitute_command_keys, 713DEFUN ("substitute-command-keys", Fsubstitute_command_keys,
701 Ssubstitute_command_keys, 1, 1, 0, 714 Ssubstitute_command_keys, 1, 1, 0,
702 doc: /* Substitute key descriptions for command names in STRING. 715 doc: /* Substitute key descriptions for command names in STRING.
@@ -751,20 +764,7 @@ Otherwise, return a new string. */)
751 name = Qnil; 764 name = Qnil;
752 GCPRO4 (string, tem, keymap, name); 765 GCPRO4 (string, tem, keymap, name);
753 766
754 enum { unicode, grave_accent, apostrophe } quote_translation = unicode; 767 enum text_quoting_style quoting_style = text_quoting_style ();
755 if (EQ (Vtext_quoting_style, Qgrave))
756 quote_translation = grave_accent;
757 else if (EQ (Vtext_quoting_style, Qstraight))
758 quote_translation = apostrophe;
759 else if (NILP (Vtext_quoting_style)
760 && DISP_TABLE_P (Vstandard_display_table))
761 {
762 Lisp_Object dv = DISP_CHAR_VECTOR (XCHAR_TABLE (Vstandard_display_table),
763 LEFT_SINGLE_QUOTATION_MARK);
764 if (VECTORP (dv) && ASIZE (dv) == 1
765 && EQ (AREF (dv, 0), make_number ('`')))
766 quote_translation = grave_accent;
767 }
768 768
769 multibyte = STRING_MULTIBYTE (string); 769 multibyte = STRING_MULTIBYTE (string);
770 nchars = 0; 770 nchars = 0;
@@ -966,7 +966,7 @@ Otherwise, return a new string. */)
966 strp = SDATA (string) + idx; 966 strp = SDATA (string) + idx;
967 } 967 }
968 } 968 }
969 else if (strp[0] == '`' && quote_translation == unicode) 969 else if (strp[0] == '`' && quoting_style == CURVE_QUOTING_STYLE)
970 { 970 {
971 in_quote = true; 971 in_quote = true;
972 start = LSQM; 972 start = LSQM;
@@ -976,7 +976,7 @@ Otherwise, return a new string. */)
976 idx = strp - SDATA (string) + 1; 976 idx = strp - SDATA (string) + 1;
977 goto subst; 977 goto subst;
978 } 978 }
979 else if (strp[0] == '`' && quote_translation == apostrophe) 979 else if (strp[0] == '`' && quoting_style == STRAIGHT_QUOTING_STYLE)
980 { 980 {
981 *bufp++ = '\''; 981 *bufp++ = '\'';
982 strp++; 982 strp++;
@@ -991,9 +991,9 @@ Otherwise, return a new string. */)
991 } 991 }
992 else if (strp[0] == uLSQM0 && strp[1] == uLSQM1 992 else if (strp[0] == uLSQM0 && strp[1] == uLSQM1
993 && (strp[2] == uLSQM2 || strp[2] == uRSQM2) 993 && (strp[2] == uLSQM2 || strp[2] == uRSQM2)
994 && quote_translation != unicode) 994 && quoting_style != CURVE_QUOTING_STYLE)
995 { 995 {
996 *bufp++ = (strp[2] == uLSQM2 && quote_translation == grave_accent 996 *bufp++ = (strp[2] == uLSQM2 && quoting_style == GRAVE_QUOTING_STYLE
997 ? '`' : '\''); 997 ? '`' : '\'');
998 strp += 3; 998 strp += 3;
999 nchars++; 999 nchars++;
diff --git a/src/editfns.c b/src/editfns.c
index 9ff39f9bf19..ed57d8aee09 100644
--- a/src/editfns.c
+++ b/src/editfns.c
@@ -3800,8 +3800,9 @@ DEFUN ("format", Fformat, Sformat, 1, MANY, 0,
3800The first argument is a format control string. 3800The first argument is a format control string.
3801The other arguments are substituted into it to make the result, a string. 3801The other arguments are substituted into it to make the result, a string.
3802 3802
3803The format control string may contain %-sequences meaning to substitute 3803The format control string may contain ordinary characters,
3804the next available argument: 3804%-sequences meaning to substitute the next available argument,
3805and curved single quotation marks meaning to substitute quotes.
3805 3806
3806%s means print a string argument. Actually, prints any object, with `princ'. 3807%s means print a string argument. Actually, prints any object, with `princ'.
3807%d means print as number in decimal (%o octal, %x hex). 3808%d means print as number in decimal (%o octal, %x hex).
@@ -3846,6 +3847,12 @@ precision specifier says how many decimal places to show; if zero, the
3846decimal point itself is omitted. For %s and %S, the precision 3847decimal point itself is omitted. For %s and %S, the precision
3847specifier truncates the string to the given width. 3848specifier truncates the string to the given width.
3848 3849
3850\\=‘ and \\=’ means print left and right quotes as per
3851‘text-quoting-style’.
3852
3853Return the first argument if it contains no format directives.
3854Otherwise, return a new string.
3855
3849usage: (format STRING &rest OBJECTS) */) 3856usage: (format STRING &rest OBJECTS) */)
3850 (ptrdiff_t nargs, Lisp_Object *args) 3857 (ptrdiff_t nargs, Lisp_Object *args)
3851{ 3858{
@@ -3858,6 +3865,7 @@ usage: (format STRING &rest OBJECTS) */)
3858 ptrdiff_t buf_save_value_index IF_LINT (= 0); 3865 ptrdiff_t buf_save_value_index IF_LINT (= 0);
3859 char *format, *end, *format_start; 3866 char *format, *end, *format_start;
3860 ptrdiff_t formatlen, nchars; 3867 ptrdiff_t formatlen, nchars;
3868 bool changed = false;
3861 /* True if the format is multibyte. */ 3869 /* True if the format is multibyte. */
3862 bool multibyte_format = 0; 3870 bool multibyte_format = 0;
3863 /* True if the output should be a multibyte string, 3871 /* True if the output should be a multibyte string,
@@ -3921,6 +3929,8 @@ usage: (format STRING &rest OBJECTS) */)
3921 if (STRINGP (args[n]) && STRING_MULTIBYTE (args[n])) 3929 if (STRINGP (args[n]) && STRING_MULTIBYTE (args[n]))
3922 multibyte = 1; 3930 multibyte = 1;
3923 3931
3932 enum text_quoting_style quoting_style = text_quoting_style ();
3933
3924 /* If we start out planning a unibyte result, 3934 /* If we start out planning a unibyte result,
3925 then discover it has to be multibyte, we jump back to retry. */ 3935 then discover it has to be multibyte, we jump back to retry. */
3926 retry: 3936 retry:
@@ -4005,6 +4015,7 @@ usage: (format STRING &rest OBJECTS) */)
4005 if (format == end) 4015 if (format == end)
4006 error ("Format string ends in middle of format specifier"); 4016 error ("Format string ends in middle of format specifier");
4007 4017
4018 changed = true;
4008 memset (&discarded[format0 - format_start], 1, format - format0); 4019 memset (&discarded[format0 - format_start], 1, format - format0);
4009 conversion = *format; 4020 conversion = *format;
4010 if (conversion == '%') 4021 if (conversion == '%')
@@ -4426,6 +4437,20 @@ usage: (format STRING &rest OBJECTS) */)
4426 4437
4427 convbytes = format - src; 4438 convbytes = format - src;
4428 memset (&discarded[src + 1 - format_start], 2, convbytes - 1); 4439 memset (&discarded[src + 1 - format_start], 2, convbytes - 1);
4440
4441 if (quoting_style != CURVE_QUOTING_STYLE && convbytes == 3
4442 && (unsigned char) src[0] == uLSQM0
4443 && (unsigned char) src[1] == uLSQM1
4444 && ((unsigned char) src[2] == uLSQM2
4445 || (unsigned char) src[2] == uRSQM2))
4446 {
4447 convbytes = 1;
4448 str[0] = (((unsigned char) src[2] == uLSQM2
4449 && quoting_style == GRAVE_QUOTING_STYLE)
4450 ? '`' : '\'');
4451 src = (char *) str;
4452 changed = true;
4453 }
4429 } 4454 }
4430 else 4455 else
4431 { 4456 {
@@ -4437,6 +4462,7 @@ usage: (format STRING &rest OBJECTS) */)
4437 int c = BYTE8_TO_CHAR (uc); 4462 int c = BYTE8_TO_CHAR (uc);
4438 convbytes = CHAR_STRING (c, str); 4463 convbytes = CHAR_STRING (c, str);
4439 src = (char *) str; 4464 src = (char *) str;
4465 changed = true;
4440 } 4466 }
4441 } 4467 }
4442 4468
@@ -4484,113 +4510,119 @@ usage: (format STRING &rest OBJECTS) */)
4484 if (bufsize < p - buf) 4510 if (bufsize < p - buf)
4485 emacs_abort (); 4511 emacs_abort ();
4486 4512
4487 if (maybe_combine_byte) 4513 if (!changed)
4488 nchars = multibyte_chars_in_text ((unsigned char *) buf, p - buf); 4514 val = args[0];
4489 val = make_specified_string (buf, nchars, p - buf, multibyte); 4515 else
4490
4491 /* If the format string has text properties, or any of the string
4492 arguments has text properties, set up text properties of the
4493 result string. */
4494
4495 if (string_intervals (args[0]) || arg_intervals)
4496 { 4516 {
4497 Lisp_Object len, new_len, props; 4517 if (maybe_combine_byte)
4498 struct gcpro gcpro1; 4518 nchars = multibyte_chars_in_text ((unsigned char *) buf, p - buf);
4519 val = make_specified_string (buf, nchars, p - buf, multibyte);
4499 4520
4500 /* Add text properties from the format string. */ 4521 /* If the format string has text properties, or any of the string
4501 len = make_number (SCHARS (args[0])); 4522 arguments has text properties, set up text properties of the
4502 props = text_property_list (args[0], make_number (0), len, Qnil); 4523 result string. */
4503 GCPRO1 (props);
4504 4524
4505 if (CONSP (props)) 4525 if (string_intervals (args[0]) || arg_intervals)
4506 { 4526 {
4507 ptrdiff_t bytepos = 0, position = 0, translated = 0; 4527 Lisp_Object len, new_len, props;
4508 ptrdiff_t argn = 1; 4528 struct gcpro gcpro1;
4509 Lisp_Object list; 4529
4510 4530 /* Add text properties from the format string. */
4511 /* Adjust the bounds of each text property 4531 len = make_number (SCHARS (args[0]));
4512 to the proper start and end in the output string. */ 4532 props = text_property_list (args[0], make_number (0), len, Qnil);
4513 4533 GCPRO1 (props);
4514 /* Put the positions in PROPS in increasing order, so that 4534
4515 we can do (effectively) one scan through the position 4535 if (CONSP (props))
4516 space of the format string. */
4517 props = Fnreverse (props);
4518
4519 /* BYTEPOS is the byte position in the format string,
4520 POSITION is the untranslated char position in it,
4521 TRANSLATED is the translated char position in BUF,
4522 and ARGN is the number of the next arg we will come to. */
4523 for (list = props; CONSP (list); list = XCDR (list))
4524 { 4536 {
4525 Lisp_Object item; 4537 ptrdiff_t bytepos = 0, position = 0, translated = 0;
4526 ptrdiff_t pos; 4538 ptrdiff_t argn = 1;
4539 Lisp_Object list;
4540
4541 /* Adjust the bounds of each text property
4542 to the proper start and end in the output string. */
4543
4544 /* Put the positions in PROPS in increasing order, so that
4545 we can do (effectively) one scan through the position
4546 space of the format string. */
4547 props = Fnreverse (props);
4548
4549 /* BYTEPOS is the byte position in the format string,
4550 POSITION is the untranslated char position in it,
4551 TRANSLATED is the translated char position in BUF,
4552 and ARGN is the number of the next arg we will come to. */
4553 for (list = props; CONSP (list); list = XCDR (list))
4554 {
4555 Lisp_Object item;
4556 ptrdiff_t pos;
4527 4557
4528 item = XCAR (list); 4558 item = XCAR (list);
4529 4559
4530 /* First adjust the property start position. */ 4560 /* First adjust the property start position. */
4531 pos = XINT (XCAR (item)); 4561 pos = XINT (XCAR (item));
4532 4562
4533 /* Advance BYTEPOS, POSITION, TRANSLATED and ARGN 4563 /* Advance BYTEPOS, POSITION, TRANSLATED and ARGN
4534 up to this position. */ 4564 up to this position. */
4535 for (; position < pos; bytepos++) 4565 for (; position < pos; bytepos++)
4536 {
4537 if (! discarded[bytepos])
4538 position++, translated++;
4539 else if (discarded[bytepos] == 1)
4540 { 4566 {
4541 position++; 4567 if (! discarded[bytepos])
4542 if (translated == info[argn].start) 4568 position++, translated++;
4569 else if (discarded[bytepos] == 1)
4543 { 4570 {
4544 translated += info[argn].end - info[argn].start; 4571 position++;
4545 argn++; 4572 if (translated == info[argn].start)
4573 {
4574 translated += info[argn].end - info[argn].start;
4575 argn++;
4576 }
4546 } 4577 }
4547 } 4578 }
4548 }
4549 4579
4550 XSETCAR (item, make_number (translated)); 4580 XSETCAR (item, make_number (translated));
4551 4581
4552 /* Likewise adjust the property end position. */ 4582 /* Likewise adjust the property end position. */
4553 pos = XINT (XCAR (XCDR (item))); 4583 pos = XINT (XCAR (XCDR (item)));
4554 4584
4555 for (; position < pos; bytepos++) 4585 for (; position < pos; bytepos++)
4556 {
4557 if (! discarded[bytepos])
4558 position++, translated++;
4559 else if (discarded[bytepos] == 1)
4560 { 4586 {
4561 position++; 4587 if (! discarded[bytepos])
4562 if (translated == info[argn].start) 4588 position++, translated++;
4589 else if (discarded[bytepos] == 1)
4563 { 4590 {
4564 translated += info[argn].end - info[argn].start; 4591 position++;
4565 argn++; 4592 if (translated == info[argn].start)
4593 {
4594 translated += info[argn].end - info[argn].start;
4595 argn++;
4596 }
4566 } 4597 }
4567 } 4598 }
4599
4600 XSETCAR (XCDR (item), make_number (translated));
4568 } 4601 }
4569 4602
4570 XSETCAR (XCDR (item), make_number (translated)); 4603 add_text_properties_from_list (val, props, make_number (0));
4571 } 4604 }
4572 4605
4573 add_text_properties_from_list (val, props, make_number (0)); 4606 /* Add text properties from arguments. */
4574 } 4607 if (arg_intervals)
4575 4608 for (n = 1; n < nargs; ++n)
4576 /* Add text properties from arguments. */ 4609 if (info[n].intervals)
4577 if (arg_intervals) 4610 {
4578 for (n = 1; n < nargs; ++n) 4611 len = make_number (SCHARS (args[n]));
4579 if (info[n].intervals) 4612 new_len = make_number (info[n].end - info[n].start);
4580 { 4613 props = text_property_list (args[n], make_number (0),
4581 len = make_number (SCHARS (args[n])); 4614 len, Qnil);
4582 new_len = make_number (info[n].end - info[n].start); 4615 props = extend_property_ranges (props, new_len);
4583 props = text_property_list (args[n], make_number (0), len, Qnil); 4616 /* If successive arguments have properties, be sure that
4584 props = extend_property_ranges (props, new_len); 4617 the value of `composition' property be the copy. */
4585 /* If successive arguments have properties, be sure that 4618 if (n > 1 && info[n - 1].end)
4586 the value of `composition' property be the copy. */ 4619 make_composition_value_copy (props);
4587 if (n > 1 && info[n - 1].end) 4620 add_text_properties_from_list (val, props,
4588 make_composition_value_copy (props); 4621 make_number (info[n].start));
4589 add_text_properties_from_list (val, props, 4622 }
4590 make_number (info[n].start));
4591 }
4592 4623
4593 UNGCPRO; 4624 UNGCPRO;
4625 }
4594 } 4626 }
4595 4627
4596 /* If we allocated BUF or INFO with malloc, free it too. */ 4628 /* If we allocated BUF or INFO with malloc, free it too. */
diff --git a/src/lisp.h b/src/lisp.h
index 2545203a674..d3dcaecd2bb 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -4300,6 +4300,25 @@ extern void set_initial_environment (void);
4300extern void syms_of_callproc (void); 4300extern void syms_of_callproc (void);
4301 4301
4302/* Defined in doc.c. */ 4302/* Defined in doc.c. */
4303enum
4304 {
4305 /* Named constants for the UTF-8 encodings of U+2018 LEFT SINGLE
4306 QUOTATION MARK and U+2019 RIGHT SINGLE QUOTATION MARK. */
4307 uLSQM0 = 0xE2, uLSQM1 = 0x80, uLSQM2 = 0x98,
4308 uRSQM0 = 0xE2, uRSQM1 = 0x80, uRSQM2 = 0x99
4309 };
4310enum text_quoting_style
4311 {
4312 /* Use curved single quotes ‘like this’. */
4313 CURVE_QUOTING_STYLE,
4314
4315 /* Use grave accent and apostrophe `like this'. */
4316 GRAVE_QUOTING_STYLE,
4317
4318 /* Use apostrophes 'like this'. */
4319 STRAIGHT_QUOTING_STYLE
4320 };
4321extern enum text_quoting_style text_quoting_style (void);
4303extern Lisp_Object read_doc_string (Lisp_Object); 4322extern Lisp_Object read_doc_string (Lisp_Object);
4304extern Lisp_Object get_doc_string (Lisp_Object, bool, bool); 4323extern Lisp_Object get_doc_string (Lisp_Object, bool, bool);
4305extern void syms_of_doc (void); 4324extern void syms_of_doc (void);