From 905cb83dec024e9691ca888b006566c3b3042e72 Mon Sep 17 00:00:00 2001 From: Philipp Stephani Date: Sun, 23 Jul 2017 21:58:49 +0200 Subject: [PATCH] Electric quote mode: Conditionally replace " (Bug#24710) * lisp/electric.el (electric-quote-replace-double): New user option. (electric-quote-post-self-insert-function): Use it. * electric-tests.el (electric-quote-replace-double-disabled) (electric-quote-replace-double-enabled) (electric-quote-context-sensitive-bob-replace-double) (electric-quote-context-sensitive-bol-replace-double) (electric-quote-context-sensitive-after-space-replace-double) (electric-quote-context-sensitive-after-letter-replace-double) (electric-quote-context-sensitive-after-paren-replace-double): New unit tests. --- etc/NEWS | 5 ++++ lisp/electric.el | 24 ++++++++++++++++--- test/lisp/electric-tests.el | 56 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+), 3 deletions(-) diff --git a/etc/NEWS b/etc/NEWS index 4cb02bf518..25b9b7eb03 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -157,6 +157,11 @@ 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 users option 'electric-quote-replace-double' controls +whether a double quote is also replaced in 'electric-quote-mode'. If +non-nil, a double ASCII quote is replaced by a double typographic +quote. + ** The new variable 'electric-quote-inhibit-functions' controls when to disable electric quoting based on context. Major modes can add functions to this list; Emacs will temporarily disable diff --git a/lisp/electric.el b/lisp/electric.el index a71e79ff78..0ad6b284e4 100644 --- a/lisp/electric.el +++ b/lisp/electric.el @@ -451,6 +451,16 @@ electric-quote-context-sensitive :version "26.1" :type 'boolean :safe #'booleanp :group 'electricity) +(defcustom electric-quote-replace-double nil + "Non-nil means to replace \" with an electric double quote. +If `electric-quote-context-sensitive' is nil, Emacs replaces \" +with a closing double quote. Otherwise, it replaces \" with an +opening double quote after a line break, whitespace, opening +parenthesis, or quote, and with a closing double quote +otherwise." + :version "26.1" + :type 'boolean :safe #'booleanp :group 'electricity) + (defvar electric-quote-inhibit-functions () "List of functions that should inhibit electric quoting. When the variable `electric-quote-mode' is non-nil, Emacs will @@ -467,7 +477,9 @@ electric-quote-post-self-insert-function (when (and electric-quote-mode (or (eq last-command-event ?\') (and (not electric-quote-context-sensitive) - (eq last-command-event ?\`))) + (eq last-command-event ?\`)) + (and electric-quote-replace-double + (eq last-command-event ?\"))) (not (run-hook-with-args-until-success 'electric-quote-inhibit-functions)) (if (derived-mode-p 'text-mode) @@ -506,13 +518,19 @@ electric-quote-post-self-insert-function (setq last-command-event q<<)) ((search-backward (string backtick) (1- (point)) t) (replace-match (string q<)) - (setq last-command-event q<))) + (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>)))))))))) + (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 c6ffccc079..5b4b98b802 100644 --- a/test/lisp/electric-tests.el +++ b/test/lisp/electric-tests.el @@ -617,6 +617,19 @@ electric-quote-closing-double :fixture-fn #'electric-quote-local-mode :test-in-comments nil :test-in-strings nil) +(define-electric-pair-test electric-quote-replace-double-disabled + "" "\"" :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-replace-double-enabled + "" "\"" :expected-string "”" :expected-point 2 + :modes '(text-mode) + :fixture-fn #'electric-quote-local-mode + :bindings '((electric-quote-replace-double . t)) + :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) @@ -638,6 +651,14 @@ electric-quote-context-sensitive-bob-double :bindings '((electric-quote-context-sensitive . t)) :test-in-comments nil :test-in-strings nil) +(define-electric-pair-test electric-quote-context-sensitive-bob-replace-double + "" "\"" :expected-string "“" :expected-point 2 + :modes '(text-mode) + :fixture-fn #'electric-quote-local-mode + :bindings '((electric-quote-context-sensitive . t) + (electric-quote-replace-double . 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) @@ -652,6 +673,14 @@ electric-quote-context-sensitive-bol-double :bindings '((electric-quote-context-sensitive . t)) :test-in-comments nil :test-in-strings nil) +(define-electric-pair-test electric-quote-context-sensitive-bol-replace-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) + (electric-quote-replace-double . 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) @@ -666,6 +695,15 @@ electric-quote-context-sensitive-after-space-double :bindings '((electric-quote-context-sensitive . t)) :test-in-comments nil :test-in-strings nil) +(define-electric-pair-test + electric-quote-context-sensitive-after-space-replace-double + " " "-\"" :expected-string " “" :expected-point 3 + :modes '(text-mode) + :fixture-fn #'electric-quote-local-mode + :bindings '((electric-quote-context-sensitive . t) + (electric-quote-replace-double . 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) @@ -680,6 +718,15 @@ electric-quote-context-sensitive-after-letter-double :bindings '((electric-quote-context-sensitive . t)) :test-in-comments nil :test-in-strings nil) +(define-electric-pair-test + electric-quote-context-sensitive-after-letter-replace-double + "a" "-\"" :expected-string "a”" :expected-point 3 + :modes '(text-mode) + :fixture-fn #'electric-quote-local-mode + :bindings '((electric-quote-context-sensitive . t) + (electric-quote-replace-double . 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) @@ -694,6 +741,15 @@ electric-quote-context-sensitive-after-paren-double :bindings '((electric-quote-context-sensitive . t)) :test-in-comments nil :test-in-strings nil) +(define-electric-pair-test + electric-quote-context-sensitive-after-paren-replace-double + "(" "-\"" :expected-string "(“" :expected-point 3 + :modes '(text-mode) + :fixture-fn #'electric-quote-local-mode + :bindings '((electric-quote-context-sensitive . t) + (electric-quote-replace-double . t)) + :test-in-comments nil :test-in-strings nil) + ;; Simulate ‘markdown-mode’: it sets both ‘comment-start’ and ;; ‘comment-use-syntax’, but derives from ‘text-mode’. (define-electric-pair-test electric-quote-markdown-in-text -- 2.13.3