diff options
| -rw-r--r-- | doc/lispref/help.texi | 12 | ||||
| -rw-r--r-- | doc/lispref/strings.texi | 73 | ||||
| -rw-r--r-- | etc/NEWS | 25 | ||||
| -rw-r--r-- | lisp/calc/calc-help.el | 2 | ||||
| -rw-r--r-- | lisp/emacs-lisp/derived.el | 5 | ||||
| -rw-r--r-- | lisp/info.el | 6 | ||||
| -rw-r--r-- | src/doc.c | 52 | ||||
| -rw-r--r-- | src/editfns.c | 202 | ||||
| -rw-r--r-- | src/lisp.h | 19 |
9 files changed, 240 insertions, 156 deletions
diff --git a/doc/lispref/help.texi b/doc/lispref/help.texi index ca8ae3f314a..ab1696e6712 100644 --- a/doc/lispref/help.texi +++ b/doc/lispref/help.texi | |||
| @@ -347,19 +347,11 @@ and @samp{\=\=} puts @samp{\=} into the output. | |||
| 347 | @strong{Please note:} Each @samp{\} must be doubled when written in a | 347 | @strong{Please note:} Each @samp{\} must be doubled when written in a |
| 348 | string in Emacs Lisp. | 348 | string in Emacs Lisp. |
| 349 | 349 | ||
| 350 | @defvar text-quoting-style | ||
| 351 | @cindex curved quotes | 350 | @cindex curved quotes |
| 352 | @cindex curly quotes | 351 | @cindex curly quotes |
| 353 | The value of this variable specifies the style | 352 | The value of the @code{text-quoting-style} variable specifies the style |
| 354 | @code{substitute-command-keys} uses when generating left and right | 353 | @code{substitute-command-keys} uses when generating left and right |
| 355 | quotes. If the variable's value is @code{curve}, the style is | 354 | quotes. @xref{Formatting Strings}, for more information. |
| 356 | @t{‘like this’} with curved single quotes. If the value is | ||
| 357 | @code{straight}, the style is @t{'like this'} with straight | ||
| 358 | apostrophes. If the value is @code{grave}, the style is @t{`like | ||
| 359 | this'} with grave accent and apostrophe. The default value @code{nil} | ||
| 360 | acts like @code{curve} if curved single quotes are displayable, and | ||
| 361 | like @code{grave} otherwise. | ||
| 362 | @end defvar | ||
| 363 | 355 | ||
| 364 | @defun substitute-command-keys string | 356 | @defun substitute-command-keys string |
| 365 | This function scans @var{string} for the above special sequences and | 357 | This function scans @var{string} for the above special sequences and |
diff --git a/doc/lispref/strings.texi b/doc/lispref/strings.texi index c2f06079cb6..30933387b20 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 | |||
| 805 | in how they use the result of formatting. | 805 | in how they use the result of formatting. |
| 806 | 806 | ||
| 807 | @defun format string &rest objects | 807 | @defun format string &rest objects |
| 808 | This function returns a new string that is made by copying | 808 | This function returns a string that is equivalent to copying |
| 809 | @var{string} and then replacing any format specification | 809 | @var{string} and then replacing any format specification |
| 810 | in the copy with encodings of the corresponding @var{objects}. The | 810 | in the copy with encodings of the corresponding @var{objects}. The |
| 811 | arguments @var{objects} are the computed values to be formatted. | 811 | arguments @var{objects} are the computed values to be formatted. |
| 812 | 812 | ||
| 813 | The characters in @var{string}, other than the format specifications, | 813 | The characters in @var{string}, other than the format specifications, |
| 814 | are copied directly into the output, including their text properties, | 814 | are copied directly into the output, including their text properties, |
| 815 | if any. | 815 | if any. If the output equals @var{string}, this function may return |
| 816 | @var{string} itself rather than a new copy. | ||
| 816 | @end defun | 817 | @end defun |
| 817 | 818 | ||
| 818 | @cindex @samp{%} in format | 819 | @cindex @samp{%} in format |
| 819 | @cindex format specification | 820 | @cindex format specification |
| 821 | @cindex curved quotes | ||
| 822 | @cindex curly quotes | ||
| 820 | A format specification is a sequence of characters beginning with a | 823 | A format specification is a sequence of characters beginning with a |
| 821 | @samp{%}. Thus, if there is a @samp{%d} in @var{string}, the | 824 | @samp{%} or is a curved single quotation mark. Except for @samp{%%} |
| 822 | @code{format} function replaces it with the printed representation of | 825 | and quotation marks, each format specification says how to represent |
| 823 | one of the values to be formatted (one of the arguments @var{objects}). | 826 | one of the arguments @var{objects}. For example, if there |
| 827 | is a @samp{%d} in @var{string}, the @code{format} function replaces it | ||
| 828 | with the decimal representation of the integer to be formatted. | ||
| 824 | For example: | 829 | For example: |
| 825 | 830 | ||
| 826 | @example | 831 | @example |
| @@ -830,11 +835,12 @@ For example: | |||
| 830 | @end group | 835 | @end group |
| 831 | @end example | 836 | @end example |
| 832 | 837 | ||
| 833 | Since @code{format} interprets @samp{%} characters as format | 838 | Since @code{format} interprets @samp{%}, @samp{‘} and @samp{’} |
| 839 | characters as format | ||
| 834 | specifications, you should @emph{never} pass an arbitrary string as | 840 | specifications, you should @emph{never} pass an arbitrary string as |
| 835 | the first argument. This is particularly true when the string is | 841 | the first argument. This is particularly true when the string is |
| 836 | generated by some Lisp code. Unless the string is @emph{known} to | 842 | generated by some Lisp code. Unless the string is @emph{known} to |
| 837 | never include any @samp{%} characters, pass @code{"%s"}, described | 843 | never include any of the three special characters, pass @code{"%s"}, described |
| 838 | below, as the first argument, and the string as the second, like this: | 844 | below, as the first argument, and the string as the second, like this: |
| 839 | 845 | ||
| 840 | @example | 846 | @example |
| @@ -908,20 +914,30 @@ is shorter. | |||
| 908 | Replace the specification with a single @samp{%}. This format | 914 | Replace the specification with a single @samp{%}. This format |
| 909 | specification is unusual in that it does not use a value. For example, | 915 | specification is unusual in that it does not use a value. For example, |
| 910 | @code{(format "%% %d" 30)} returns @code{"% 30"}. | 916 | @code{(format "%% %d" 30)} returns @code{"% 30"}. |
| 917 | |||
| 918 | @item ‘ | ||
| 919 | @itemx ’ | ||
| 920 | @cindex curved quotes | ||
| 921 | @cindex curly quotes | ||
| 922 | Replace the specification with a left or right quote, respectively. | ||
| 923 | Although typically a curved single quotation mark stands for itself, | ||
| 924 | other quoting styles are available as per the variable | ||
| 925 | @samp{text-quoting-style} described below. | ||
| 911 | @end table | 926 | @end table |
| 912 | 927 | ||
| 913 | Any other format character results in an @samp{Invalid format | 928 | Any other format character after @samp{%} results in an @samp{Invalid format |
| 914 | operation} error. | 929 | operation} error. |
| 915 | 930 | ||
| 916 | Here are several examples: | 931 | Here are several examples, which assume the typical quoting style |
| 932 | where curved single quotes stand for themselves: | ||
| 917 | 933 | ||
| 918 | @example | 934 | @example |
| 919 | @group | 935 | @group |
| 920 | (format "The name of this buffer is %s." (buffer-name)) | 936 | (format "The name of this buffer is ‘%s’." (buffer-name)) |
| 921 | @result{} "The name of this buffer is strings.texi." | 937 | @result{} "The name of this buffer is ‘strings.texi’." |
| 922 | 938 | ||
| 923 | (format "The buffer object prints as %s." (current-buffer)) | 939 | (format "The buffer object prints as ‘%s’." (current-buffer)) |
| 924 | @result{} "The buffer object prints as strings.texi." | 940 | @result{} "The buffer object prints as ‘strings.texi’." |
| 925 | 941 | ||
| 926 | (format "The octal value of %d is %o, | 942 | (format "The octal value of %d is %o, |
| 927 | and the hex value is %x." 18 18 18) | 943 | and the hex value is %x." 18 18 18) |
| @@ -932,7 +948,7 @@ operation} error. | |||
| 932 | 948 | ||
| 933 | @cindex field width | 949 | @cindex field width |
| 934 | @cindex padding | 950 | @cindex padding |
| 935 | A specification can have a @dfn{width}, which is a decimal number | 951 | A @samp{%} specification can have a @dfn{width}, which is a decimal number |
| 936 | between the @samp{%} and the specification character. If the printed | 952 | between the @samp{%} and the specification character. If the printed |
| 937 | representation of the object contains fewer characters than this | 953 | representation of the object contains fewer characters than this |
| 938 | width, @code{format} extends it with padding. The width specifier is | 954 | width, @code{format} extends it with padding. The width specifier is |
| @@ -948,7 +964,7 @@ the width specifier normally consists of spaces inserted on the left: | |||
| 948 | If the width is too small, @code{format} does not truncate the | 964 | If the width is too small, @code{format} does not truncate the |
| 949 | object's printed representation. Thus, you can use a width to specify | 965 | object's printed representation. Thus, you can use a width to specify |
| 950 | a minimum spacing between columns with no risk of losing information. | 966 | a minimum spacing between columns with no risk of losing information. |
| 951 | In the following three examples, @samp{%7s} specifies a minimum width | 967 | In the following two examples, @samp{%7s} specifies a minimum width |
| 952 | of 7. In the first case, the string inserted in place of @samp{%7s} | 968 | of 7. In the first case, the string inserted in place of @samp{%7s} |
| 953 | has only 3 letters, and needs 4 blank spaces as padding. In the | 969 | has only 3 letters, and needs 4 blank spaces as padding. In the |
| 954 | second case, the string @code{"specification"} is 13 letters wide but | 970 | second case, the string @code{"specification"} is 13 letters wide but |
| @@ -956,12 +972,12 @@ is not truncated. | |||
| 956 | 972 | ||
| 957 | @example | 973 | @example |
| 958 | @group | 974 | @group |
| 959 | (format "The word '%7s' has %d letters in it." | 975 | (format "The word ‘%7s’ has %d letters in it." |
| 960 | "foo" (length "foo")) | 976 | "foo" (length "foo")) |
| 961 | @result{} "The word ' foo' has 3 letters in it." | 977 | @result{} "The word ‘ foo’ has 3 letters in it." |
| 962 | (format "The word '%7s' has %d letters in it." | 978 | (format "The word ‘%7s’ has %d letters in it." |
| 963 | "specification" (length "specification")) | 979 | "specification" (length "specification")) |
| 964 | @result{} "The word 'specification' has 13 letters in it." | 980 | @result{} "The word ‘specification’ has 13 letters in it." |
| 965 | @end group | 981 | @end group |
| 966 | @end example | 982 | @end example |
| 967 | 983 | ||
| @@ -1003,14 +1019,14 @@ ignored. | |||
| 1003 | (format "%-6d is padded on the right" 123) | 1019 | (format "%-6d is padded on the right" 123) |
| 1004 | @result{} "123 is padded on the right" | 1020 | @result{} "123 is padded on the right" |
| 1005 | 1021 | ||
| 1006 | (format "The word '%-7s' actually has %d letters in it." | 1022 | (format "The word ‘%-7s’ actually has %d letters in it." |
| 1007 | "foo" (length "foo")) | 1023 | "foo" (length "foo")) |
| 1008 | @result{} "The word 'foo ' actually has 3 letters in it." | 1024 | @result{} "The word ‘foo ’ actually has 3 letters in it." |
| 1009 | @end group | 1025 | @end group |
| 1010 | @end example | 1026 | @end example |
| 1011 | 1027 | ||
| 1012 | @cindex precision in format specifications | 1028 | @cindex precision in format specifications |
| 1013 | All the specification characters allow an optional @dfn{precision} | 1029 | The @samp{%} specification characters allow an optional @dfn{precision} |
| 1014 | before the character (after the width, if present). The precision is | 1030 | before the character (after the width, if present). The precision is |
| 1015 | a decimal-point @samp{.} followed by a digit-string. For the | 1031 | a decimal-point @samp{.} followed by a digit-string. For the |
| 1016 | floating-point specifications (@samp{%e}, @samp{%f}, @samp{%g}), the | 1032 | floating-point specifications (@samp{%e}, @samp{%f}, @samp{%g}), the |
| @@ -1021,6 +1037,19 @@ shows only the first three characters of the representation for | |||
| 1021 | @var{object}. Precision has no effect for other specification | 1037 | @var{object}. Precision has no effect for other specification |
| 1022 | characters. | 1038 | characters. |
| 1023 | 1039 | ||
| 1040 | @defvar text-quoting-style | ||
| 1041 | @cindex curved quotes | ||
| 1042 | @cindex curly quotes | ||
| 1043 | This variable specifies the style @code{format} uses when generating | ||
| 1044 | left and right quotes. If the value is @code{curve}, the style is | ||
| 1045 | @t{‘like this’} with curved single quotes. If the value is | ||
| 1046 | @code{straight}, the style is @t{'like this'} with straight | ||
| 1047 | apostrophes. If the value is @code{grave}, the style is @t{`like | ||
| 1048 | this'} with grave accent and apostrophe. The default value @code{nil} | ||
| 1049 | acts like @code{curve} if curved single quotes are displayable, and | ||
| 1050 | like @code{grave} otherwise. | ||
| 1051 | @end defvar | ||
| 1052 | |||
| 1024 | @node Case Conversion | 1053 | @node Case Conversion |
| 1025 | @section Case Conversion in Lisp | 1054 | @section Case Conversion in Lisp |
| 1026 | @cindex upper case | 1055 | @cindex upper case |
| @@ -895,16 +895,25 @@ denied" instead of "permission denied". The old behavior was problematic | |||
| 895 | in languages like German where downcasing rules depend on grammar. | 895 | in languages like German where downcasing rules depend on grammar. |
| 896 | 896 | ||
| 897 | +++ | 897 | +++ |
| 898 | ** ‘format’ now replaces curved single quotes. | ||
| 899 | That is, it replaces strings' curved single quotes (also known as | ||
| 900 | curly quotes) as per the value of the new custom variable | ||
| 901 | ‘text-quoting-style’: ‘curve’ means replace curved quotes with | ||
| 902 | themselves ‘like this’, ‘straight’ means use straight apostrophes | ||
| 903 | 'like this', ‘grave’ means use grave accent and apostrophe `like | ||
| 904 | this', and nil (default) means use curved quotes if displayable and | ||
| 905 | grave accent and apostrophe otherwise. Because it now may be used | ||
| 906 | in many contexts where it's a no-op, ‘format’ is no longer required to | ||
| 907 | create a string, and may return its first argument if the argument | ||
| 908 | already has the correct value. | ||
| 909 | |||
| 910 | +++ | ||
| 898 | ** substitute-command-keys now replaces quotes. | 911 | ** substitute-command-keys now replaces quotes. |
| 899 | That is, it converts documentation strings' quoting style as per the | 912 | That is, it converts documentation strings' quoting style as per the |
| 900 | value of the new custom variable ‘text-quoting-style’: ‘curve’ means | 913 | value of ‘text-quoting-style’ as described above. Doc strings in |
| 901 | use curved quotes (also known as curly quotes) ‘like this’, ‘straight’ | 914 | source code can use either curved quotes or grave accent and |
| 902 | means use straight apostrophes 'like this', ‘grave’ means use grave | 915 | apostrophe. As before, isolated apostrophes and characters preceded |
| 903 | accent and apostrophe `like this', and nil (default) means use curved | 916 | by \= are output as-is. |
| 904 | quotes if displayable and grave accent and apostrophe otherwise. Doc | ||
| 905 | strings in source code can use either curved quotes or grave accent | ||
| 906 | and apostrophe. As before, isolated apostrophes and characters | ||
| 907 | preceded by \= are output as-is. | ||
| 908 | 917 | ||
| 909 | +++ | 918 | +++ |
| 910 | ** The character classes [:alpha:] and [:alnum:] in regular expressions | 919 | ** The character classes [:alpha:] and [:alnum:] in regular expressions |
diff --git a/lisp/calc/calc-help.el b/lisp/calc/calc-help.el index b2e7df1b718..7a1a983e6b3 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." | |||
| 365 | (let (Info-history) | 365 | (let (Info-history) |
| 366 | (Info-goto-node (buffer-substring (match-beginning 1) (match-end 1)))) | 366 | (Info-goto-node (buffer-substring (match-beginning 1) (match-end 1)))) |
| 367 | (let* ((string-target (or target thing)) | 367 | (let* ((string-target (or target thing)) |
| 368 | (quoted (format "['`‘]%s['’]" (regexp-quote string-target))) | 368 | (quoted (concat "['`‘]" (regexp-quote string-target) "['’]")) |
| 369 | (bracketed (format "\\[%s\\]\\|(%s)\\|\\<The[ \n]%s" | 369 | (bracketed (format "\\[%s\\]\\|(%s)\\|\\<The[ \n]%s" |
| 370 | quoted quoted quoted))) | 370 | quoted quoted quoted))) |
| 371 | (or (let ((case-fold-search nil)) | 371 | (or (let ((case-fold-search nil)) |
diff --git a/lisp/emacs-lisp/derived.el b/lisp/emacs-lisp/derived.el index ee137f1771e..1f8572b278b 100644 --- a/lisp/emacs-lisp/derived.el +++ b/lisp/emacs-lisp/derived.el | |||
| @@ -331,9 +331,10 @@ which more-or-less shadow%s %s's corresponding table%s." | |||
| 331 | "\n\nThis mode " | 331 | "\n\nThis mode " |
| 332 | (concat | 332 | (concat |
| 333 | "\n\nIn addition to any hooks its parent mode " | 333 | "\n\nIn addition to any hooks its parent mode " |
| 334 | (if (string-match (format "[`‘]%s['’]" | 334 | (if (string-match (concat "[`%‘]" |
| 335 | (regexp-quote | 335 | (regexp-quote |
| 336 | (symbol-name parent))) | 336 | (symbol-name parent)) |
| 337 | "['%’]") | ||
| 337 | docstring) | 338 | docstring) |
| 338 | nil | 339 | nil |
| 339 | (format "`%s' " parent)) | 340 | (format "`%s' " parent)) |
diff --git a/lisp/info.el b/lisp/info.el index 0a2206d61bf..1b02c3585b1 100644 --- a/lisp/info.el +++ b/lisp/info.el | |||
| @@ -3398,10 +3398,12 @@ Give an empty topic name to go to the Index node itself." | |||
| 3398 | (re-search-forward (format | 3398 | (re-search-forward (format |
| 3399 | "[a-zA-Z]+: [a-zA-Z0-9_ *&]+ %s\\( \\|$\\)" | 3399 | "[a-zA-Z]+: [a-zA-Z0-9_ *&]+ %s\\( \\|$\\)" |
| 3400 | (regexp-quote name)) nil t) | 3400 | (regexp-quote name)) nil t) |
| 3401 | (search-forward (format "['`‘]%s['’]" name) nil t) | 3401 | (search-forward (concat "['`‘]" name "['’]") nil t) |
| 3402 | (and (string-match "\\`.*\\( (.*)\\)\\'" name) | 3402 | (and (string-match "\\`.*\\( (.*)\\)\\'" name) |
| 3403 | (search-forward | 3403 | (search-forward |
| 3404 | (format "['`‘]%s['’]" (substring name 0 (match-beginning 1))) | 3404 | (concat "['`%‘]" |
| 3405 | (substring name 0 (match-beginning 1)) | ||
| 3406 | "['%’]") | ||
| 3405 | nil t)) | 3407 | nil t)) |
| 3406 | (search-forward name nil t) | 3408 | (search-forward name nil t) |
| 3407 | ;; Try again without the " <1>" makeinfo can append | 3409 | ;; Try again without the " <1>" makeinfo can append |
| @@ -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. */ | ||
| 690 | enum | ||
| 691 | { | ||
| 692 | uLSQM0 = 0xE2, uLSQM1 = 0x80, uLSQM2 = 0x98, | ||
| 693 | uRSQM0 = 0xE2, uRSQM1 = 0x80, uRSQM2 = 0x99, | ||
| 694 | }; | ||
| 695 | static unsigned char const LSQM[] = { uLSQM0, uLSQM1, uLSQM2 }; | 688 | static unsigned char const LSQM[] = { uLSQM0, uLSQM1, uLSQM2 }; |
| 696 | static unsigned char const RSQM[] = { uRSQM0, uRSQM1, uRSQM2 }; | 689 | static 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. */ | ||
| 694 | enum text_quoting_style | ||
| 695 | text_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 | |||
| 700 | DEFUN ("substitute-command-keys", Fsubstitute_command_keys, | 713 | DEFUN ("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, | |||
| 3800 | The first argument is a format control string. | 3800 | The first argument is a format control string. |
| 3801 | The other arguments are substituted into it to make the result, a string. | 3801 | The other arguments are substituted into it to make the result, a string. |
| 3802 | 3802 | ||
| 3803 | The format control string may contain %-sequences meaning to substitute | 3803 | The format control string may contain ordinary characters, |
| 3804 | the next available argument: | 3804 | %-sequences meaning to substitute the next available argument, |
| 3805 | and 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 | |||
| 3846 | decimal point itself is omitted. For %s and %S, the precision | 3847 | decimal point itself is omitted. For %s and %S, the precision |
| 3847 | specifier truncates the string to the given width. | 3848 | specifier truncates the string to the given width. |
| 3848 | 3849 | ||
| 3850 | \\=‘ and \\=’ means print left and right quotes as per | ||
| 3851 | ‘text-quoting-style’. | ||
| 3852 | |||
| 3853 | Return the first argument if it contains no format directives. | ||
| 3854 | Otherwise, return a new string. | ||
| 3855 | |||
| 3849 | usage: (format STRING &rest OBJECTS) */) | 3856 | usage: (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); | |||
| 4300 | extern void syms_of_callproc (void); | 4300 | extern void syms_of_callproc (void); |
| 4301 | 4301 | ||
| 4302 | /* Defined in doc.c. */ | 4302 | /* Defined in doc.c. */ |
| 4303 | enum | ||
| 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 | }; | ||
| 4310 | enum 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 | }; | ||
| 4321 | extern enum text_quoting_style text_quoting_style (void); | ||
| 4303 | extern Lisp_Object read_doc_string (Lisp_Object); | 4322 | extern Lisp_Object read_doc_string (Lisp_Object); |
| 4304 | extern Lisp_Object get_doc_string (Lisp_Object, bool, bool); | 4323 | extern Lisp_Object get_doc_string (Lisp_Object, bool, bool); |
| 4305 | extern void syms_of_doc (void); | 4324 | extern void syms_of_doc (void); |