diff options
| author | Philipp Stephani | 2017-07-23 21:58:49 +0200 |
|---|---|---|
| committer | Philipp Stephani | 2017-10-01 00:20:36 +0200 |
| commit | d247e1d30abcb77665f829ca98a5bdef69ff4bc3 (patch) | |
| tree | db9c7b1127eaa1860fbc586c8eace53ea4e3c4f2 | |
| parent | d88a0f6554888643854ddb2c1f49b77b0bf8904c (diff) | |
| download | emacs-d247e1d30abcb77665f829ca98a5bdef69ff4bc3.tar.gz emacs-d247e1d30abcb77665f829ca98a5bdef69ff4bc3.zip | |
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.
* test/lisp/electric-tests.el (electric-quote-replace-double-disabled)
(electric-quote-replace-double-bob)
(electric-quote-replace-double-bol)
(electric-quote-replace-double-after-space)
(electric-quote-replace-double-after-letter)
(electric-quote-replace-double-after-paren): New unit tests.
* doc/emacs/text.texi (Quotation Marks): Document
'electric-quote-replace-double'.
| -rw-r--r-- | doc/emacs/text.texi | 7 | ||||
| -rw-r--r-- | etc/NEWS | 5 | ||||
| -rw-r--r-- | lisp/electric.el | 25 | ||||
| -rw-r--r-- | test/lisp/electric-tests.el | 41 |
4 files changed, 74 insertions, 4 deletions
diff --git a/doc/emacs/text.texi b/doc/emacs/text.texi index 496b43ce1e3..5aa0c77d34c 100644 --- a/doc/emacs/text.texi +++ b/doc/emacs/text.texi | |||
| @@ -443,6 +443,13 @@ non-@code{nil}, and in programming-language strings if | |||
| 443 | @code{nil} for @code{electric-quote-string} and @code{t} for the other | 443 | @code{nil} for @code{electric-quote-string} and @code{t} for the other |
| 444 | variables. | 444 | variables. |
| 445 | 445 | ||
| 446 | @vindex electric-quote-replace-double | ||
| 447 | You can also set the option @code{electric-quote-replace-double} to | ||
| 448 | a non-@code{nil} value. Then, typing @t{"} insert an appropriate | ||
| 449 | curved double quote depending on context: @t{“} at the beginning of | ||
| 450 | the buffer or after a line break, whitespace, opening parenthesis, or | ||
| 451 | quote character, and @t{”} otherwise. | ||
| 452 | |||
| 446 | Electric Quote mode is disabled by default. To toggle it, type | 453 | Electric Quote mode is disabled by default. To toggle it, type |
| 447 | @kbd{M-x electric-quote-mode}. To toggle it in a single buffer, use | 454 | @kbd{M-x electric-quote-mode}. To toggle it in a single buffer, use |
| 448 | @kbd{M-x electric-quote-local-mode}. To suppress it for a single use, | 455 | @kbd{M-x electric-quote-local-mode}. To suppress it for a single use, |
| @@ -42,6 +42,11 @@ When you add a new item, use the appropriate mark if you are sure it applies, | |||
| 42 | This controls how long Emacs will wait for updates to the graphical | 42 | This controls how long Emacs will wait for updates to the graphical |
| 43 | state to take effect (making a frame visible, for example). | 43 | state to take effect (making a frame visible, for example). |
| 44 | 44 | ||
| 45 | +++ | ||
| 46 | ** The new user option 'electric-quote-replace-double' controls | ||
| 47 | whether " is also replaced in 'electric-quote-mode'. If non-nil, " is | ||
| 48 | replaced by a double typographic quote. | ||
| 49 | |||
| 45 | 50 | ||
| 46 | * Changes in Specialized Modes and Packages in Emacs 27.1 | 51 | * Changes in Specialized Modes and Packages in Emacs 27.1 |
| 47 | 52 | ||
diff --git a/lisp/electric.el b/lisp/electric.el index d7929945db2..65e36b7a63f 100644 --- a/lisp/electric.el +++ b/lisp/electric.el | |||
| @@ -451,6 +451,14 @@ whitespace, opening parenthesis, or quote and leaves \\=` alone." | |||
| 451 | :version "26.1" | 451 | :version "26.1" |
| 452 | :type 'boolean :safe #'booleanp :group 'electricity) | 452 | :type 'boolean :safe #'booleanp :group 'electricity) |
| 453 | 453 | ||
| 454 | (defcustom electric-quote-replace-double nil | ||
| 455 | "Non-nil means to replace \" with an electric double quote. | ||
| 456 | Emacs replaces \" with an opening double quote after a line | ||
| 457 | break, whitespace, opening parenthesis, or quote, and with a | ||
| 458 | closing double quote otherwise." | ||
| 459 | :version "26.1" | ||
| 460 | :type 'boolean :safe #'booleanp :group 'electricity) | ||
| 461 | |||
| 454 | (defvar electric-quote-inhibit-functions () | 462 | (defvar electric-quote-inhibit-functions () |
| 455 | "List of functions that should inhibit electric quoting. | 463 | "List of functions that should inhibit electric quoting. |
| 456 | When the variable `electric-quote-mode' is non-nil, Emacs will | 464 | When the variable `electric-quote-mode' is non-nil, Emacs will |
| @@ -467,7 +475,9 @@ This requotes when a quoting key is typed." | |||
| 467 | (when (and electric-quote-mode | 475 | (when (and electric-quote-mode |
| 468 | (or (eq last-command-event ?\') | 476 | (or (eq last-command-event ?\') |
| 469 | (and (not electric-quote-context-sensitive) | 477 | (and (not electric-quote-context-sensitive) |
| 470 | (eq last-command-event ?\`))) | 478 | (eq last-command-event ?\`)) |
| 479 | (and electric-quote-replace-double | ||
| 480 | (eq last-command-event ?\"))) | ||
| 471 | (not (run-hook-with-args-until-success | 481 | (not (run-hook-with-args-until-success |
| 472 | 'electric-quote-inhibit-functions)) | 482 | 'electric-quote-inhibit-functions)) |
| 473 | (if (derived-mode-p 'text-mode) | 483 | (if (derived-mode-p 'text-mode) |
| @@ -488,7 +498,8 @@ This requotes when a quoting key is typed." | |||
| 488 | (save-excursion | 498 | (save-excursion |
| 489 | (let ((backtick ?\`)) | 499 | (let ((backtick ?\`)) |
| 490 | (if (or (eq last-command-event ?\`) | 500 | (if (or (eq last-command-event ?\`) |
| 491 | (and electric-quote-context-sensitive | 501 | (and (or electric-quote-context-sensitive |
| 502 | electric-quote-replace-double) | ||
| 492 | (save-excursion | 503 | (save-excursion |
| 493 | (backward-char) | 504 | (backward-char) |
| 494 | (or (bobp) (bolp) | 505 | (or (bobp) (bolp) |
| @@ -506,13 +517,19 @@ This requotes when a quoting key is typed." | |||
| 506 | (setq last-command-event q<<)) | 517 | (setq last-command-event q<<)) |
| 507 | ((search-backward (string backtick) (1- (point)) t) | 518 | ((search-backward (string backtick) (1- (point)) t) |
| 508 | (replace-match (string q<)) | 519 | (replace-match (string q<)) |
| 509 | (setq last-command-event q<))) | 520 | (setq last-command-event q<)) |
| 521 | ((search-backward "\"" (1- (point)) t) | ||
| 522 | (replace-match (string q<<)) | ||
| 523 | (setq last-command-event q<<))) | ||
| 510 | (cond ((search-backward (string q> ?') (- (point) 2) t) | 524 | (cond ((search-backward (string q> ?') (- (point) 2) t) |
| 511 | (replace-match (string q>>)) | 525 | (replace-match (string q>>)) |
| 512 | (setq last-command-event q>>)) | 526 | (setq last-command-event q>>)) |
| 513 | ((search-backward "'" (1- (point)) t) | 527 | ((search-backward "'" (1- (point)) t) |
| 514 | (replace-match (string q>)) | 528 | (replace-match (string q>)) |
| 515 | (setq last-command-event q>)))))))))) | 529 | (setq last-command-event q>)) |
| 530 | ((search-backward "\"" (1- (point)) t) | ||
| 531 | (replace-match (string q>>)) | ||
| 532 | (setq last-command-event q>>)))))))))) | ||
| 516 | 533 | ||
| 517 | (put 'electric-quote-post-self-insert-function 'priority 10) | 534 | (put 'electric-quote-post-self-insert-function 'priority 10) |
| 518 | 535 | ||
diff --git a/test/lisp/electric-tests.el b/test/lisp/electric-tests.el index fc69919fbe1..7df2449b9eb 100644 --- a/test/lisp/electric-tests.el +++ b/test/lisp/electric-tests.el | |||
| @@ -617,6 +617,12 @@ baz\"\"" | |||
| 617 | :fixture-fn #'electric-quote-local-mode | 617 | :fixture-fn #'electric-quote-local-mode |
| 618 | :test-in-comments nil :test-in-strings nil) | 618 | :test-in-comments nil :test-in-strings nil) |
| 619 | 619 | ||
| 620 | (define-electric-pair-test electric-quote-replace-double-disabled | ||
| 621 | "" "\"" :expected-string "\"" :expected-point 2 | ||
| 622 | :modes '(text-mode) | ||
| 623 | :fixture-fn #'electric-quote-local-mode | ||
| 624 | :test-in-comments nil :test-in-strings nil) | ||
| 625 | |||
| 620 | (define-electric-pair-test electric-quote-context-sensitive-backtick | 626 | (define-electric-pair-test electric-quote-context-sensitive-backtick |
| 621 | "" "`" :expected-string "`" :expected-point 2 | 627 | "" "`" :expected-string "`" :expected-point 2 |
| 622 | :modes '(text-mode) | 628 | :modes '(text-mode) |
| @@ -638,6 +644,13 @@ baz\"\"" | |||
| 638 | :bindings '((electric-quote-context-sensitive . t)) | 644 | :bindings '((electric-quote-context-sensitive . t)) |
| 639 | :test-in-comments nil :test-in-strings nil) | 645 | :test-in-comments nil :test-in-strings nil) |
| 640 | 646 | ||
| 647 | (define-electric-pair-test electric-quote-replace-double-bob | ||
| 648 | "" "\"" :expected-string "“" :expected-point 2 | ||
| 649 | :modes '(text-mode) | ||
| 650 | :fixture-fn #'electric-quote-local-mode | ||
| 651 | :bindings '((electric-quote-replace-double . t)) | ||
| 652 | :test-in-comments nil :test-in-strings nil) | ||
| 653 | |||
| 641 | (define-electric-pair-test electric-quote-context-sensitive-bol-single | 654 | (define-electric-pair-test electric-quote-context-sensitive-bol-single |
| 642 | "a\n" "--'" :expected-string "a\n‘" :expected-point 4 | 655 | "a\n" "--'" :expected-string "a\n‘" :expected-point 4 |
| 643 | :modes '(text-mode) | 656 | :modes '(text-mode) |
| @@ -652,6 +665,13 @@ baz\"\"" | |||
| 652 | :bindings '((electric-quote-context-sensitive . t)) | 665 | :bindings '((electric-quote-context-sensitive . t)) |
| 653 | :test-in-comments nil :test-in-strings nil) | 666 | :test-in-comments nil :test-in-strings nil) |
| 654 | 667 | ||
| 668 | (define-electric-pair-test electric-quote-replace-double-bol | ||
| 669 | "a\n" "--\"" :expected-string "a\n“" :expected-point 4 | ||
| 670 | :modes '(text-mode) | ||
| 671 | :fixture-fn #'electric-quote-local-mode | ||
| 672 | :bindings '((electric-quote-replace-double . t)) | ||
| 673 | :test-in-comments nil :test-in-strings nil) | ||
| 674 | |||
| 655 | (define-electric-pair-test electric-quote-context-sensitive-after-space-single | 675 | (define-electric-pair-test electric-quote-context-sensitive-after-space-single |
| 656 | " " "-'" :expected-string " ‘" :expected-point 3 | 676 | " " "-'" :expected-string " ‘" :expected-point 3 |
| 657 | :modes '(text-mode) | 677 | :modes '(text-mode) |
| @@ -666,6 +686,13 @@ baz\"\"" | |||
| 666 | :bindings '((electric-quote-context-sensitive . t)) | 686 | :bindings '((electric-quote-context-sensitive . t)) |
| 667 | :test-in-comments nil :test-in-strings nil) | 687 | :test-in-comments nil :test-in-strings nil) |
| 668 | 688 | ||
| 689 | (define-electric-pair-test electric-quote-replace-double-after-space | ||
| 690 | " " "-\"" :expected-string " “" :expected-point 3 | ||
| 691 | :modes '(text-mode) | ||
| 692 | :fixture-fn #'electric-quote-local-mode | ||
| 693 | :bindings '((electric-quote-replace-double . t)) | ||
| 694 | :test-in-comments nil :test-in-strings nil) | ||
| 695 | |||
| 669 | (define-electric-pair-test electric-quote-context-sensitive-after-letter-single | 696 | (define-electric-pair-test electric-quote-context-sensitive-after-letter-single |
| 670 | "a" "-'" :expected-string "a’" :expected-point 3 | 697 | "a" "-'" :expected-string "a’" :expected-point 3 |
| 671 | :modes '(text-mode) | 698 | :modes '(text-mode) |
| @@ -680,6 +707,13 @@ baz\"\"" | |||
| 680 | :bindings '((electric-quote-context-sensitive . t)) | 707 | :bindings '((electric-quote-context-sensitive . t)) |
| 681 | :test-in-comments nil :test-in-strings nil) | 708 | :test-in-comments nil :test-in-strings nil) |
| 682 | 709 | ||
| 710 | (define-electric-pair-test electric-quote-replace-double-after-letter | ||
| 711 | "a" "-\"" :expected-string "a”" :expected-point 3 | ||
| 712 | :modes '(text-mode) | ||
| 713 | :fixture-fn #'electric-quote-local-mode | ||
| 714 | :bindings '((electric-quote-replace-double . t)) | ||
| 715 | :test-in-comments nil :test-in-strings nil) | ||
| 716 | |||
| 683 | (define-electric-pair-test electric-quote-context-sensitive-after-paren-single | 717 | (define-electric-pair-test electric-quote-context-sensitive-after-paren-single |
| 684 | "(" "-'" :expected-string "(‘" :expected-point 3 | 718 | "(" "-'" :expected-string "(‘" :expected-point 3 |
| 685 | :modes '(text-mode) | 719 | :modes '(text-mode) |
| @@ -694,6 +728,13 @@ baz\"\"" | |||
| 694 | :bindings '((electric-quote-context-sensitive . t)) | 728 | :bindings '((electric-quote-context-sensitive . t)) |
| 695 | :test-in-comments nil :test-in-strings nil) | 729 | :test-in-comments nil :test-in-strings nil) |
| 696 | 730 | ||
| 731 | (define-electric-pair-test electric-quote-replace-double-after-paren | ||
| 732 | "(" "-\"" :expected-string "(“" :expected-point 3 | ||
| 733 | :modes '(text-mode) | ||
| 734 | :fixture-fn #'electric-quote-local-mode | ||
| 735 | :bindings '((electric-quote-replace-double . t)) | ||
| 736 | :test-in-comments nil :test-in-strings nil) | ||
| 737 | |||
| 697 | ;; Simulate ‘markdown-mode’: it sets both ‘comment-start’ and | 738 | ;; Simulate ‘markdown-mode’: it sets both ‘comment-start’ and |
| 698 | ;; ‘comment-use-syntax’, but derives from ‘text-mode’. | 739 | ;; ‘comment-use-syntax’, but derives from ‘text-mode’. |
| 699 | (define-electric-pair-test electric-quote-markdown-in-text | 740 | (define-electric-pair-test electric-quote-markdown-in-text |