From 68280c5ee9b87d874ffa7c111b3cac7e634cee22 Mon Sep 17 00:00:00 2001
From: Paul Eggert <eggert@cs.ucla.edu>
Date: Mon, 24 Aug 2015 23:37:18 -0700
Subject: [PATCH] =?utf8?q?Treat=20'=20like=20=E2=80=99=20even=20when=20not?=
 =?utf8?q?=20matching=20`?=
MIME-Version: 1.0
Content-Type: text/plain; charset=utf8
Content-Transfer-Encoding: 8bit

This is simpler and easier to explain, and should encourage better
typography.  Do this in Electric Quote mode and when translating
quotes in docstrings.  Inspired by a suggestion by Dmitry Gutov	in:
https://lists.gnu.org/archive/html/emacs-devel/2015-08/msg00806.html
* doc/emacs/text.texi (Quotation Marks):
* doc/lispref/help.texi (Keys in Documentation):
* etc/NEWS:
Document this.
* lisp/electric.el (electric-quote-post-self-insert-function):
* src/doc.c (Fsubstitute_command_keys):
Always treat ' like ’ even when not matched by an open quote.
---
 doc/emacs/text.texi   | 12 +++++-------
 doc/lispref/help.texi | 20 ++++----------------
 etc/NEWS              |  2 +-
 lisp/electric.el      | 33 ++++++++++++++-------------------
 src/doc.c             | 25 ++++++++-----------------
 5 files changed, 32 insertions(+), 60 deletions(-)

diff --git a/doc/emacs/text.texi b/doc/emacs/text.texi
index 5a13f208876..31760b70b4c 100644
--- a/doc/emacs/text.texi
+++ b/doc/emacs/text.texi
@@ -420,13 +420,11 @@ left and right single or double quotation marks @t{‘like this’} or
 @t{“like this”}.  Typewriter quotes are simple and portable; curved
 quotes are less ambiguous and typically look nicer.
 
-  Electric Quote mode makes it easier to type curved quotes.  It
-optionally converts a quotation's grave accent and apostrophe @t{`like
-this'} to single quotation marks @t{‘like this’}.  Similarly, it
-converts a quotation's double grave accent and double apostrophe
-@t{``like this''} to double quotation marks @t{“like this”}.  These
-conversions are suppressed in buffers whose coding systems cannot
-represent curved quote characters.
+  Electric Quote mode makes it easier to type curved quotes.  As you
+type characters it optionally converts @t{`} to @t{‘}, @t{'} to @t{’},
+@t{``} to @t{“}, and @t{''} to @t{”}.  These conversions are
+suppressed in buffers whose coding systems cannot represent curved
+quote characters.
 
 @vindex electric-quote-paragraph
 @vindex electric-quote-comment
diff --git a/doc/lispref/help.texi b/doc/lispref/help.texi
index ab884f8dc80..44c09a2085a 100644
--- a/doc/lispref/help.texi
+++ b/doc/lispref/help.texi
@@ -318,25 +318,13 @@ 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 quote, and alters the interpretation
-of the next unmatched apostrophe.
-
-@item '
-(apostrophe) stands for a right quote if preceded by grave accent and
-there are no intervening apostrophes.  Otherwise, apostrophe stands
-for itself.
-
 @item ‘
-(left single quotation mark) stands for a left quote.
+@itemx `
+(left single quotation mark and grave accent) both stand for a left quote.
 
 @item ’
-(right single quotation mark) stands for a right quote.
-
-@item '
-(apostrophe) stands for a right quote if
-preceded by grave accent and there are no intervening apostrophes.
-Otherwise, apostrophe stands for itself.
+@itemx '
+(right single quotation mark and apostrophe) both stand for a right quote.
 
 @item \=
 quotes the following character and is discarded; thus, @samp{\=`} puts
diff --git a/etc/NEWS b/etc/NEWS
index fd2ed4dd8af..89ab98d0692 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -923,7 +923,7 @@ Quotes in info files are not translated.
 That is, it converts documentation strings’ quoting style as per the
 value of ‘text-quoting-style’.  Doc strings in source code can use
 either curved quotes or grave accent and apostrophe.  As before,
-isolated apostrophes and characters preceded by \= are output as-is.
+characters preceded by \= are output as-is.
 
 +++
 ** Message-issuing functions ‘error’, ‘message’, etc. now convert quotes.
diff --git a/lisp/electric.el b/lisp/electric.el
index 8ca09316bcb..bef5bb9fec7 100644
--- a/lisp/electric.el
+++ b/lisp/electric.el
@@ -453,7 +453,7 @@ This requotes when a quoting key is typed."
         (save-excursion
           (if (eq last-command-event ?\`)
               (cond ((and (electric--insertable-p "“")
-                          (re-search-backward "[`‘]`" (- (point) 2) t))
+                          (search-backward "‘`" (- (point) 2) t))
                      (replace-match "“")
                      (when (and electric-pair-mode
                                 (eq (cdr-safe
@@ -465,19 +465,14 @@ This requotes when a quoting key is typed."
                           (search-backward "`" (1- (point)) t))
                      (replace-match "‘")
                      (setq last-command-event ?‘)))
-            (let ((pos (point)))
-              (if (memq (char-before (1- (point))) '(?\' ?’))
-                  (when (and (search-backward "“" start t)
-                             (eq pos (re-search-forward
-                                      "“\\(\\([^‘”]\\|‘[^‘’”]*’\\)*\\)['’]'"
-                                      pos t)))
-                    (replace-match "“\\1”")
-                    (setq last-command-event ?”))
-                (when (and (search-backward "‘" start t)
-                           (eq pos (re-search-forward
-                                    "‘\\([^’]*\\)'" pos t)))
-                  (replace-match "‘\\1’")
-                  (setq last-command-event ?’))))))))))
+            (cond ((and (electric--insertable-p "”")
+                        (search-backward "’'" (- (point) 2) t))
+                   (replace-match "”")
+                   (setq last-command-event ?”))
+                  ((and (electric--insertable-p "’")
+                        (search-backward "'" (1- (point)) t))
+                   (replace-match "’")
+                   (setq last-command-event ?’)))))))))
 
 (put 'electric-quote-post-self-insert-function 'priority 10)
 
@@ -488,11 +483,11 @@ With a prefix argument ARG, enable Electric Quote mode if
 ARG is positive, and disable it otherwise.  If called from Lisp,
 enable the mode if ARG is omitted or nil.
 
-When enabled, this replaces \\=`foo bar' with \\=‘foo bar\\=’ and replaces
-\\=`\\=`foo bar'' with “foo bar” as you type.  This occurs only in
-comments, strings, and text paragraphs, and these are selectively
-controlled with ‘electric-quote-comment’,
-‘electric-quote-string’, and ‘electric-quote-paragraph’.
+When enabled, as you type this replaces \\=` with \\=‘, \\=' with \\=’,
+\\=`\\=` with “, and \\='\\=' with ”.  This occurs only in comments, strings,
+and text paragraphs, and these are selectively controlled with
+‘electric-quote-comment’, ‘electric-quote-string’, and
+‘electric-quote-paragraph’.
 
 This is a global minor mode.  To toggle the mode in a single buffer,
 use ‘electric-quote-local-mode’."
diff --git a/src/doc.c b/src/doc.c
index 3c8b11d73f3..7a298e28631 100644
--- a/src/doc.c
+++ b/src/doc.c
@@ -731,10 +731,9 @@ summary).
 Each substring of the form \\=\\<MAPVAR> specifies the use of MAPVAR
 as the keymap for future \\=\\[COMMAND] substrings.
 
-Each \\=‘ and \\=’ are replaced by left and right quote.  Each \\=` is
-replaced by left quote, and each ' preceded by \\=` and without
-intervening ' is replaced by right quote.  Left and right quote
-characters are specified by ‘text-quoting-style’.
+Each \\=‘ and \\=` is replaced by left quote, and each \\=’ and \\='
+is replaced by right quote.  Left and right quote characters are
+specified by ‘text-quoting-style’.
 
 \\=\\= quotes the following character and is discarded; thus,
 \\=\\=\\=\\= puts \\=\\= into the output, \\=\\=\\=\\[ puts \\=\\[ into the output, and
@@ -746,7 +745,6 @@ Otherwise, return a new string.  */)
 {
   char *buf;
   bool changed = false;
-  bool in_quote = false;
   unsigned char *strp;
   char *bufp;
   ptrdiff_t idx;
@@ -971,11 +969,10 @@ Otherwise, return a new string.  */)
 	    strp = SDATA (string) + idx;
 	  }
 	}
-      else if (strp[0] == '`' && quoting_style == CURVE_QUOTING_STYLE)
+      else if ((strp[0] == '`' || strp[0] == '\'')
+	       && quoting_style == CURVE_QUOTING_STYLE)
 	{
-	  in_quote = true;
-	  start = LSQM;
-	subst_quote:
+	  start = strp[0] == '`' ? LSQM : RSQM;
 	  length = 1;
 	  length_byte = 3;
 	  idx = strp - SDATA (string) + 1;
@@ -988,12 +985,6 @@ Otherwise, return a new string.  */)
 	  nchars++;
 	  changed = true;
 	}
-      else if (strp[0] == '\'' && in_quote)
-	{
-	  in_quote = false;
-	  start = RSQM;
-	  goto subst_quote;
-	}
       else if (strp[0] == uLSQM0 && strp[1] == uLSQM1
 	       && (strp[2] == uLSQM2 || strp[2] == uRSQM2)
 	       && quoting_style != CURVE_QUOTING_STYLE)
@@ -1108,8 +1099,8 @@ syms_of_doc (void)
   DEFVAR_LISP ("text-quoting-style", Vtext_quoting_style,
                doc: /* Style to use for single quotes when generating text.
 ‘curve’ means quote with curved single quotes \\=‘like this\\=’.
-‘straight’ means quote with straight apostrophes 'like this'.
-‘grave’ means quote with grave accent and apostrophe \\=`like this'.
+‘straight’ means quote with straight apostrophes \\='like this\\='.
+‘grave’ means quote with grave accent and apostrophe \\=`like this\\='.
 The default value nil acts like ‘curve’ if curved single quotes are
 displayable, and like ‘grave’ otherwise.  */);
   Vtext_quoting_style = Qnil;
-- 
2.39.5