mirror of
git://git.sv.gnu.org/emacs.git
synced 2025-12-15 10:30:25 -08:00
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.
This commit is contained in:
parent
d90b98a2a5
commit
34d4720f83
3 changed files with 179 additions and 19 deletions
16
etc/NEWS
16
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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue