emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Emacs-diffs] master 34d4720: Electric quotes: Improve support for Markd


From: Philipp Stephani
Subject: [Emacs-diffs] master 34d4720: Electric quotes: Improve support for Markdown mode (Bug#24709)
Date: Sun, 2 Jul 2017 11:48:51 -0400 (EDT)

branch: master
commit 34d4720f833bb382b28d9faecf82d34db1eb4494
Author: Philipp Stephani <address@hidden>
Commit: Philipp Stephani <address@hidden>

    Electric quotes: Improve support for Markdown mode (Bug#24709)
    
    Introduce a new user option 'electric-quote-context-sensitive'.  If
    non-nil, have ' insert an opening quote if sensible.
    
    Also introduce a new variable 'electric-quote-code-faces'.  Major
    modes such as 'markdown-mode' can add faces to this list to treat text
    as inline code and disable electric quoting.
    
    * lisp/electric.el (electric-quote-context-sensitive): New user
    option.
    (electric-quote-code-faces): New variable.
    (electric-quote-post-self-insert-function): Treat ' as ` if
    desired and applicable; disable electric quoting for given faces.
    
    * test/lisp/electric-tests.el (electric-quote-opening-single)
    (electric-quote-closing-single, electric-quote-opening-double)
    (electric-quote-closing-double)
    (electric-quote-context-sensitive-backtick)
    (electric-quote-context-sensitive-bob-single)
    (electric-quote-context-sensitive-bob-double)
    (electric-quote-context-sensitive-bol-single)
    (electric-quote-context-sensitive-bol-double)
    (electric-quote-context-sensitive-after-space-single)
    (electric-quote-context-sensitive-after-space-double)
    (electric-quote-context-sensitive-after-letter-single)
    (electric-quote-context-sensitive-after-letter-double)
    (electric-quote-context-sensitive-after-paren-single)
    (electric-quote-context-sensitive-after-paren-double)
    (electric-quote-markdown-in-text)
    (electric-quote-markdown-in-code): New unit tests.
---
 etc/NEWS                    |  16 ++++++
 lisp/electric.el            |  66 +++++++++++++++++--------
 test/lisp/electric-tests.el | 116 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 179 insertions(+), 19 deletions(-)

diff --git a/etc/NEWS b/etc/NEWS
index b9a492c..3f68111 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -129,6 +129,22 @@ given file is on a case-insensitive filesystem.
 of curved quotes for 'electric-quote-mode', allowing user to choose
 the types of quotes to be used.
 
+** The new user option 'electric-quote-context-sensitive' makes
+'electric-quote-mode' context sensitive.  If it is non-nil, you can
+type an ASCII apostrophe to insert an opening or closing quote,
+depending on context.  Emacs will replace the apostrophe by an opening
+quote character at the beginning of the buffer, the beginning of a
+line, after a whitespace character, and after an opening parenthesis;
+and it will replace the apostrophe by a closing quote character in all
+other cases.
+
+** The new variable 'electric-quote-code-faces' controls when to
+disable electric quoting in text modes.  Major modes can add faces to
+this list; Emacs will temporarily disable 'electric-quote-mode'
+whenever point is before a character having such a face.  This is
+intended for major modes that derive from 'text-mode' but allow inline
+code segments, such as 'markdown-mode'.
+
 +++
 ** The new user variable 'dired-omit-case-fold' allows the user to
 customize the case-sensitivity of dired-omit-mode.  It defaults to
diff --git a/lisp/electric.el b/lisp/electric.el
index 4078ef8..1564df5 100644
--- a/lisp/electric.el
+++ b/lisp/electric.el
@@ -443,11 +443,24 @@ quote, left double quote, and right double quote, 
respectively."
   :version "25.1"
   :type 'boolean :safe 'booleanp :group 'electricity)
 
+(defcustom electric-quote-context-sensitive nil
+  "Non-nil means to replace \\=' with an electric quote depending on context.
+If `electric-quote-context-sensitive' is non-nil, Emacs replaces
+\\=' and \\='\\=' with an opening quote after a line break,
+whitespace, opening parenthesis, or quote and leaves \\=` alone."
+  :version "26.1"
+  :type 'boolean :safe #'booleanp :group 'electricity)
+
+(defvar electric-quote-code-faces ()
+  "List of faces to treat as inline code in `text-mode'.")
+
 (defun electric-quote-post-self-insert-function ()
   "Function that `electric-quote-mode' adds to `post-self-insert-hook'.
 This requotes when a quoting key is typed."
   (when (and electric-quote-mode
-             (memq last-command-event '(?\' ?\`)))
+             (or (eq last-command-event ?\')
+                 (and (not electric-quote-context-sensitive)
+                      (eq last-command-event ?\`))))
     (let ((start
            (if (and comment-start comment-use-syntax)
                (when (or electric-quote-comment electric-quote-string)
@@ -462,30 +475,45 @@ This requotes when a quoting key is typed."
                                          (syntax-ppss (1- (point)))))))))
              (and electric-quote-paragraph
                   (derived-mode-p 'text-mode)
+                  ;; FIXME: There should be a ‘cl-disjoint’ function.
+                  (null (cl-intersection (face-at-point nil 'multiple)
+                                         electric-quote-code-faces
+                                         :test #'eq))
+                  ;; FIXME: Why is the next form there?  It’s never
+                  ;; nil.
                   (or (eq last-command-event ?\`)
                       (save-excursion (backward-paragraph) (point)))))))
       (pcase electric-quote-chars
         (`(,q< ,q> ,q<< ,q>>)
          (when start
            (save-excursion
-             (if (eq last-command-event ?\`)
-                 (cond ((search-backward (string q< ?`) (- (point) 2) t)
-                        (replace-match (string q<<))
-                        (when (and electric-pair-mode
-                                   (eq (cdr-safe
-                                        (assq q< electric-pair-text-pairs))
-                                       (char-after)))
-                          (delete-char 1))
-                        (setq last-command-event q<<))
-                       ((search-backward "`" (1- (point)) t)
-                        (replace-match (string q<))
-                        (setq last-command-event q<)))
-               (cond ((search-backward (string q> ?') (- (point) 2) t)
-                      (replace-match (string q>>))
-                      (setq last-command-event q>>))
-                     ((search-backward "'" (1- (point)) t)
-                      (replace-match (string q>))
-                      (setq last-command-event q>)))))))))))
+             (let ((backtick ?\`))
+               (if (or (eq last-command-event ?\`)
+                       (and electric-quote-context-sensitive
+                            (save-excursion
+                              (backward-char)
+                              (or (bobp) (bolp)
+                                  (memq (char-before) (list q< q<<))
+                                  (memq (char-syntax (char-before))
+                                        '(?\s ?\())))
+                            (setq backtick ?\')))
+                   (cond ((search-backward (string q< backtick) (- (point) 2) 
t)
+                          (replace-match (string q<<))
+                          (when (and electric-pair-mode
+                                     (eq (cdr-safe
+                                          (assq q< electric-pair-text-pairs))
+                                         (char-after)))
+                            (delete-char 1))
+                          (setq last-command-event q<<))
+                         ((search-backward (string backtick) (1- (point)) t)
+                          (replace-match (string q<))
+                          (setq last-command-event q<)))
+                 (cond ((search-backward (string q> ?') (- (point) 2) t)
+                        (replace-match (string q>>))
+                        (setq last-command-event q>>))
+                       ((search-backward "'" (1- (point)) t)
+                        (replace-match (string q>))
+                        (setq last-command-event q>))))))))))))
 
 (put 'electric-quote-post-self-insert-function 'priority 10)
 
diff --git a/test/lisp/electric-tests.el b/test/lisp/electric-tests.el
index 78a3765..6f63d30 100644
--- a/test/lisp/electric-tests.el
+++ b/test/lisp/electric-tests.el
@@ -593,5 +593,121 @@ baz\"\""
   :bindings '((electric-quote-string . t))
   :test-in-comments nil :test-in-strings nil)
 
+(define-electric-pair-test electric-quote-opening-single
+  "" "`" :expected-string "‘" :expected-point 2
+  :modes '(text-mode)
+  :fixture-fn #'electric-quote-local-mode
+  :test-in-comments nil :test-in-strings nil)
+
+(define-electric-pair-test electric-quote-closing-single
+  "" "'" :expected-string "’" :expected-point 2
+  :modes '(text-mode)
+  :fixture-fn #'electric-quote-local-mode
+  :test-in-comments nil :test-in-strings nil)
+
+(define-electric-pair-test electric-quote-opening-double
+  "‘" "-`" :expected-string "“" :expected-point 2
+  :modes '(text-mode)
+  :fixture-fn #'electric-quote-local-mode
+  :test-in-comments nil :test-in-strings nil)
+
+(define-electric-pair-test electric-quote-closing-double
+  "’" "-'" :expected-string "”" :expected-point 2
+  :modes '(text-mode)
+  :fixture-fn #'electric-quote-local-mode
+  :test-in-comments nil :test-in-strings nil)
+
+(define-electric-pair-test electric-quote-context-sensitive-backtick
+  "" "`" :expected-string "`" :expected-point 2
+  :modes '(text-mode)
+  :fixture-fn #'electric-quote-local-mode
+  :bindings '((electric-quote-context-sensitive . t))
+  :test-in-comments nil :test-in-strings nil)
+
+(define-electric-pair-test electric-quote-context-sensitive-bob-single
+  "" "'" :expected-string "‘" :expected-point 2
+  :modes '(text-mode)
+  :fixture-fn #'electric-quote-local-mode
+  :bindings '((electric-quote-context-sensitive . t))
+  :test-in-comments nil :test-in-strings nil)
+
+(define-electric-pair-test electric-quote-context-sensitive-bob-double
+  "‘" "-'" :expected-string "“" :expected-point 2
+  :modes '(text-mode)
+  :fixture-fn #'electric-quote-local-mode
+  :bindings '((electric-quote-context-sensitive . t))
+  :test-in-comments nil :test-in-strings nil)
+
+(define-electric-pair-test electric-quote-context-sensitive-bol-single
+  "a\n" "--'" :expected-string "a\n‘" :expected-point 4
+  :modes '(text-mode)
+  :fixture-fn #'electric-quote-local-mode
+  :bindings '((electric-quote-context-sensitive . t))
+  :test-in-comments nil :test-in-strings nil)
+
+(define-electric-pair-test electric-quote-context-sensitive-bol-double
+  "a\n‘" "---'" :expected-string "a\n“" :expected-point 4
+  :modes '(text-mode)
+  :fixture-fn #'electric-quote-local-mode
+  :bindings '((electric-quote-context-sensitive . t))
+  :test-in-comments nil :test-in-strings nil)
+
+(define-electric-pair-test electric-quote-context-sensitive-after-space-single
+  " " "-'" :expected-string " ‘" :expected-point 3
+  :modes '(text-mode)
+  :fixture-fn #'electric-quote-local-mode
+  :bindings '((electric-quote-context-sensitive . t))
+  :test-in-comments nil :test-in-strings nil)
+
+(define-electric-pair-test electric-quote-context-sensitive-after-space-double
+  " ‘" "--'" :expected-string " “" :expected-point 3
+  :modes '(text-mode)
+  :fixture-fn #'electric-quote-local-mode
+  :bindings '((electric-quote-context-sensitive . t))
+  :test-in-comments nil :test-in-strings nil)
+
+(define-electric-pair-test electric-quote-context-sensitive-after-letter-single
+  "a" "-'" :expected-string "a’" :expected-point 3
+  :modes '(text-mode)
+  :fixture-fn #'electric-quote-local-mode
+  :bindings '((electric-quote-context-sensitive . t))
+  :test-in-comments nil :test-in-strings nil)
+
+(define-electric-pair-test electric-quote-context-sensitive-after-letter-double
+  "a’" "--'" :expected-string "a”" :expected-point 3
+  :modes '(text-mode)
+  :fixture-fn #'electric-quote-local-mode
+  :bindings '((electric-quote-context-sensitive . t))
+  :test-in-comments nil :test-in-strings nil)
+
+(define-electric-pair-test electric-quote-context-sensitive-after-paren-single
+  "(" "-'" :expected-string "(‘" :expected-point 3
+  :modes '(text-mode)
+  :fixture-fn #'electric-quote-local-mode
+  :bindings '((electric-quote-context-sensitive . t))
+  :test-in-comments nil :test-in-strings nil)
+
+(define-electric-pair-test electric-quote-context-sensitive-after-paren-double
+  "(‘" "--'" :expected-string "(“" :expected-point 3
+  :modes '(text-mode)
+  :fixture-fn #'electric-quote-local-mode
+  :bindings '((electric-quote-context-sensitive . t))
+  :test-in-comments nil :test-in-strings nil)
+
+(define-electric-pair-test electric-quote-markdown-in-text
+  "" "'" :expected-string "’" :expected-point 2
+  :modes '(text-mode)
+  :fixture-fn #'electric-quote-local-mode
+  :bindings '((electric-quote-code-faces font-lock-constant-face))
+  :test-in-comments nil :test-in-strings nil)
+
+(define-electric-pair-test electric-quote-markdown-in-code
+  #("`a`" 1 2 (face font-lock-constant-face)) "-'"
+  :expected-string "`'a`" :expected-point 3
+  :modes '(text-mode)
+  :fixture-fn #'electric-quote-local-mode
+  :bindings '((electric-quote-code-faces font-lock-constant-face))
+  :test-in-comments nil :test-in-strings nil)
+
 (provide 'electric-tests)
 ;;; electric-tests.el ends here



reply via email to

[Prev in Thread] Current Thread [Next in Thread]