aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPhilipp Stephani2017-06-28 23:47:57 +0200
committerPhilipp Stephani2017-07-02 17:48:23 +0200
commit34d4720f833bb382b28d9faecf82d34db1eb4494 (patch)
treef149e3b03da94c7db458610007e82b33ac735018
parentd90b98a2a52abf67b84aa12df282b0defec8505b (diff)
downloademacs-34d4720f833bb382b28d9faecf82d34db1eb4494.tar.gz
emacs-34d4720f833bb382b28d9faecf82d34db1eb4494.zip
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.
-rw-r--r--etc/NEWS16
-rw-r--r--lisp/electric.el66
-rw-r--r--test/lisp/electric-tests.el116
3 files changed, 179 insertions, 19 deletions
diff --git a/etc/NEWS b/etc/NEWS
index b9a492cb5ce..3f6811198d1 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -129,6 +129,22 @@ given file is on a case-insensitive filesystem.
129of curved quotes for 'electric-quote-mode', allowing user to choose 129of curved quotes for 'electric-quote-mode', allowing user to choose
130the types of quotes to be used. 130the types of quotes to be used.
131 131
132** The new user option 'electric-quote-context-sensitive' makes
133'electric-quote-mode' context sensitive. If it is non-nil, you can
134type an ASCII apostrophe to insert an opening or closing quote,
135depending on context. Emacs will replace the apostrophe by an opening
136quote character at the beginning of the buffer, the beginning of a
137line, after a whitespace character, and after an opening parenthesis;
138and it will replace the apostrophe by a closing quote character in all
139other cases.
140
141** The new variable 'electric-quote-code-faces' controls when to
142disable electric quoting in text modes. Major modes can add faces to
143this list; Emacs will temporarily disable 'electric-quote-mode'
144whenever point is before a character having such a face. This is
145intended for major modes that derive from 'text-mode' but allow inline
146code segments, such as 'markdown-mode'.
147
132+++ 148+++
133** The new user variable 'dired-omit-case-fold' allows the user to 149** The new user variable 'dired-omit-case-fold' allows the user to
134customize the case-sensitivity of dired-omit-mode. It defaults to 150customize the case-sensitivity of dired-omit-mode. It defaults to
diff --git a/lisp/electric.el b/lisp/electric.el
index 4078ef8193e..1564df5949c 100644
--- a/lisp/electric.el
+++ b/lisp/electric.el
@@ -443,11 +443,24 @@ quote, left double quote, and right double quote, respectively."
443 :version "25.1" 443 :version "25.1"
444 :type 'boolean :safe 'booleanp :group 'electricity) 444 :type 'boolean :safe 'booleanp :group 'electricity)
445 445
446(defcustom electric-quote-context-sensitive nil
447 "Non-nil means to replace \\=' with an electric quote depending on context.
448If `electric-quote-context-sensitive' is non-nil, Emacs replaces
449\\=' and \\='\\=' with an opening quote after a line break,
450whitespace, opening parenthesis, or quote and leaves \\=` alone."
451 :version "26.1"
452 :type 'boolean :safe #'booleanp :group 'electricity)
453
454(defvar electric-quote-code-faces ()
455 "List of faces to treat as inline code in `text-mode'.")
456
446(defun electric-quote-post-self-insert-function () 457(defun electric-quote-post-self-insert-function ()
447 "Function that `electric-quote-mode' adds to `post-self-insert-hook'. 458 "Function that `electric-quote-mode' adds to `post-self-insert-hook'.
448This requotes when a quoting key is typed." 459This requotes when a quoting key is typed."
449 (when (and electric-quote-mode 460 (when (and electric-quote-mode
450 (memq last-command-event '(?\' ?\`))) 461 (or (eq last-command-event ?\')
462 (and (not electric-quote-context-sensitive)
463 (eq last-command-event ?\`))))
451 (let ((start 464 (let ((start
452 (if (and comment-start comment-use-syntax) 465 (if (and comment-start comment-use-syntax)
453 (when (or electric-quote-comment electric-quote-string) 466 (when (or electric-quote-comment electric-quote-string)
@@ -462,30 +475,45 @@ This requotes when a quoting key is typed."
462 (syntax-ppss (1- (point))))))))) 475 (syntax-ppss (1- (point)))))))))
463 (and electric-quote-paragraph 476 (and electric-quote-paragraph
464 (derived-mode-p 'text-mode) 477 (derived-mode-p 'text-mode)
478 ;; FIXME: There should be a ‘cl-disjoint’ function.
479 (null (cl-intersection (face-at-point nil 'multiple)
480 electric-quote-code-faces
481 :test #'eq))
482 ;; FIXME: Why is the next form there? It’s never
483 ;; nil.
465 (or (eq last-command-event ?\`) 484 (or (eq last-command-event ?\`)
466 (save-excursion (backward-paragraph) (point))))))) 485 (save-excursion (backward-paragraph) (point)))))))
467 (pcase electric-quote-chars 486 (pcase electric-quote-chars
468 (`(,q< ,q> ,q<< ,q>>) 487 (`(,q< ,q> ,q<< ,q>>)
469 (when start 488 (when start
470 (save-excursion 489 (save-excursion
471 (if (eq last-command-event ?\`) 490 (let ((backtick ?\`))
472 (cond ((search-backward (string q< ?`) (- (point) 2) t) 491 (if (or (eq last-command-event ?\`)
473 (replace-match (string q<<)) 492 (and electric-quote-context-sensitive
474 (when (and electric-pair-mode 493 (save-excursion
475 (eq (cdr-safe 494 (backward-char)
476 (assq q< electric-pair-text-pairs)) 495 (or (bobp) (bolp)
477 (char-after))) 496 (memq (char-before) (list q< q<<))
478 (delete-char 1)) 497 (memq (char-syntax (char-before))
479 (setq last-command-event q<<)) 498 '(?\s ?\())))
480 ((search-backward "`" (1- (point)) t) 499 (setq backtick ?\')))
481 (replace-match (string q<)) 500 (cond ((search-backward (string q< backtick) (- (point) 2) t)
482 (setq last-command-event q<))) 501 (replace-match (string q<<))
483 (cond ((search-backward (string q> ?') (- (point) 2) t) 502 (when (and electric-pair-mode
484 (replace-match (string q>>)) 503 (eq (cdr-safe
485 (setq last-command-event q>>)) 504 (assq q< electric-pair-text-pairs))
486 ((search-backward "'" (1- (point)) t) 505 (char-after)))
487 (replace-match (string q>)) 506 (delete-char 1))
488 (setq last-command-event q>))))))))))) 507 (setq last-command-event q<<))
508 ((search-backward (string backtick) (1- (point)) t)
509 (replace-match (string q<))
510 (setq last-command-event q<)))
511 (cond ((search-backward (string q> ?') (- (point) 2) t)
512 (replace-match (string q>>))
513 (setq last-command-event q>>))
514 ((search-backward "'" (1- (point)) t)
515 (replace-match (string q>))
516 (setq last-command-event q>))))))))))))
489 517
490(put 'electric-quote-post-self-insert-function 'priority 10) 518(put 'electric-quote-post-self-insert-function 'priority 10)
491 519
diff --git a/test/lisp/electric-tests.el b/test/lisp/electric-tests.el
index 78a37650619..6f63d30e755 100644
--- a/test/lisp/electric-tests.el
+++ b/test/lisp/electric-tests.el
@@ -593,5 +593,121 @@ baz\"\""
593 :bindings '((electric-quote-string . t)) 593 :bindings '((electric-quote-string . t))
594 :test-in-comments nil :test-in-strings nil) 594 :test-in-comments nil :test-in-strings nil)
595 595
596(define-electric-pair-test electric-quote-opening-single
597 "" "`" :expected-string "‘" :expected-point 2
598 :modes '(text-mode)
599 :fixture-fn #'electric-quote-local-mode
600 :test-in-comments nil :test-in-strings nil)
601
602(define-electric-pair-test electric-quote-closing-single
603 "" "'" :expected-string "’" :expected-point 2
604 :modes '(text-mode)
605 :fixture-fn #'electric-quote-local-mode
606 :test-in-comments nil :test-in-strings nil)
607
608(define-electric-pair-test electric-quote-opening-double
609 "‘" "-`" :expected-string "“" :expected-point 2
610 :modes '(text-mode)
611 :fixture-fn #'electric-quote-local-mode
612 :test-in-comments nil :test-in-strings nil)
613
614(define-electric-pair-test electric-quote-closing-double
615 "’" "-'" :expected-string "”" :expected-point 2
616 :modes '(text-mode)
617 :fixture-fn #'electric-quote-local-mode
618 :test-in-comments nil :test-in-strings nil)
619
620(define-electric-pair-test electric-quote-context-sensitive-backtick
621 "" "`" :expected-string "`" :expected-point 2
622 :modes '(text-mode)
623 :fixture-fn #'electric-quote-local-mode
624 :bindings '((electric-quote-context-sensitive . t))
625 :test-in-comments nil :test-in-strings nil)
626
627(define-electric-pair-test electric-quote-context-sensitive-bob-single
628 "" "'" :expected-string "‘" :expected-point 2
629 :modes '(text-mode)
630 :fixture-fn #'electric-quote-local-mode
631 :bindings '((electric-quote-context-sensitive . t))
632 :test-in-comments nil :test-in-strings nil)
633
634(define-electric-pair-test electric-quote-context-sensitive-bob-double
635 "‘" "-'" :expected-string "“" :expected-point 2
636 :modes '(text-mode)
637 :fixture-fn #'electric-quote-local-mode
638 :bindings '((electric-quote-context-sensitive . t))
639 :test-in-comments nil :test-in-strings nil)
640
641(define-electric-pair-test electric-quote-context-sensitive-bol-single
642 "a\n" "--'" :expected-string "a\n‘" :expected-point 4
643 :modes '(text-mode)
644 :fixture-fn #'electric-quote-local-mode
645 :bindings '((electric-quote-context-sensitive . t))
646 :test-in-comments nil :test-in-strings nil)
647
648(define-electric-pair-test electric-quote-context-sensitive-bol-double
649 "a\n‘" "---'" :expected-string "a\n“" :expected-point 4
650 :modes '(text-mode)
651 :fixture-fn #'electric-quote-local-mode
652 :bindings '((electric-quote-context-sensitive . t))
653 :test-in-comments nil :test-in-strings nil)
654
655(define-electric-pair-test electric-quote-context-sensitive-after-space-single
656 " " "-'" :expected-string " ‘" :expected-point 3
657 :modes '(text-mode)
658 :fixture-fn #'electric-quote-local-mode
659 :bindings '((electric-quote-context-sensitive . t))
660 :test-in-comments nil :test-in-strings nil)
661
662(define-electric-pair-test electric-quote-context-sensitive-after-space-double
663 " ‘" "--'" :expected-string " “" :expected-point 3
664 :modes '(text-mode)
665 :fixture-fn #'electric-quote-local-mode
666 :bindings '((electric-quote-context-sensitive . t))
667 :test-in-comments nil :test-in-strings nil)
668
669(define-electric-pair-test electric-quote-context-sensitive-after-letter-single
670 "a" "-'" :expected-string "a’" :expected-point 3
671 :modes '(text-mode)
672 :fixture-fn #'electric-quote-local-mode
673 :bindings '((electric-quote-context-sensitive . t))
674 :test-in-comments nil :test-in-strings nil)
675
676(define-electric-pair-test electric-quote-context-sensitive-after-letter-double
677 "a’" "--'" :expected-string "a”" :expected-point 3
678 :modes '(text-mode)
679 :fixture-fn #'electric-quote-local-mode
680 :bindings '((electric-quote-context-sensitive . t))
681 :test-in-comments nil :test-in-strings nil)
682
683(define-electric-pair-test electric-quote-context-sensitive-after-paren-single
684 "(" "-'" :expected-string "(‘" :expected-point 3
685 :modes '(text-mode)
686 :fixture-fn #'electric-quote-local-mode
687 :bindings '((electric-quote-context-sensitive . t))
688 :test-in-comments nil :test-in-strings nil)
689
690(define-electric-pair-test electric-quote-context-sensitive-after-paren-double
691 "(‘" "--'" :expected-string "(“" :expected-point 3
692 :modes '(text-mode)
693 :fixture-fn #'electric-quote-local-mode
694 :bindings '((electric-quote-context-sensitive . t))
695 :test-in-comments nil :test-in-strings nil)
696
697(define-electric-pair-test electric-quote-markdown-in-text
698 "" "'" :expected-string "’" :expected-point 2
699 :modes '(text-mode)
700 :fixture-fn #'electric-quote-local-mode
701 :bindings '((electric-quote-code-faces font-lock-constant-face))
702 :test-in-comments nil :test-in-strings nil)
703
704(define-electric-pair-test electric-quote-markdown-in-code
705 #("`a`" 1 2 (face font-lock-constant-face)) "-'"
706 :expected-string "`'a`" :expected-point 3
707 :modes '(text-mode)
708 :fixture-fn #'electric-quote-local-mode
709 :bindings '((electric-quote-code-faces font-lock-constant-face))
710 :test-in-comments nil :test-in-strings nil)
711
596(provide 'electric-tests) 712(provide 'electric-tests)
597;;; electric-tests.el ends here 713;;; electric-tests.el ends here