>From 106bdf81a5d27e6b055da002f8f000614268a8a1 Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Sun, 10 May 2015 16:06:05 -0700 Subject: [PROPOSED PATCH 1/2] New minor mode Electric Punct * doc/emacs/basic.texi (Inserting Text): Briefly mention how to insert curved quotation marks. * doc/emacs/modes.texi (Minor Modes): Mention Electric Punct mode. * doc/emacs/text.texi (Punctuation): New section. * etc/NEWS: Mention this. * lisp/electric.el (electric-punct-comment) (electric-punct-string, electric-punct-paragraph): New custom vars. (electric-punct-post-self-insert-function): New function. (electric-punct-mode, electric-punct-local-mode): New minor modes. --- doc/emacs/basic.texi | 7 ++++ doc/emacs/modes.texi | 9 +++++ doc/emacs/text.texi | 38 ++++++++++++++++++++++ etc/NEWS | 3 +- lisp/electric.el | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 148 insertions(+), 1 deletion(-) diff --git a/doc/emacs/basic.texi b/doc/emacs/basic.texi index be45856..2ade172 100644 --- a/doc/emacs/basic.texi +++ b/doc/emacs/basic.texi @@ -127,6 +127,13 @@ sign (Unicode code-point @code{U+221E}): A numeric argument to @kbd{C-q} or @kbd{C-x 8 @key{RET}} specifies how many copies of the character to insert (@pxref{Arguments}). + In some contexts, if you type a quotation using grave accent and +apostrophe @samp{`like this'}, it is converted to a form @samp{‘like +this’} using single quotation marks. Similarly, typing a quotation +@samp{``like this''} using double grave accent and apostrophe converts +it to a form @samp{“like this”} using double quotation marks. +@xref{Punctuation}. + @node Moving Point @section Changing the Location of Point diff --git a/doc/emacs/modes.texi b/doc/emacs/modes.texi index 0e8f46a..98a8858 100644 --- a/doc/emacs/modes.texi +++ b/doc/emacs/modes.texi @@ -200,6 +200,15 @@ Auto Save mode saves the buffer contents periodically to reduce the amount of work you can lose in case of a crash. @xref{Auto Save}. @item +@cindex Electric Punct mode +@cindex mode, Electric Punct +@findex electric-punct-mode +Electric Punct mode automatically converts some punctation marks. For +example, it requotes text typed @samp{`like this'} to text @samp{‘like +this’}. You can control what kind of text it operates in, and you can +disable it entirely in individual buffers. @xref{Punctuation}. + +@item Enriched mode enables editing and saving of formatted text. @xref{Enriched Text}. diff --git a/doc/emacs/text.texi b/doc/emacs/text.texi index 9bc5ade..06eb4fd 100644 --- a/doc/emacs/text.texi +++ b/doc/emacs/text.texi @@ -69,6 +69,7 @@ for editing such pictures. * Sentences:: Moving over and killing sentences. * Paragraphs:: Moving over paragraphs. * Pages:: Moving over pages. +* Punctuation:: Inserting punctuation. * Filling:: Filling or justifying text. * Case:: Changing the case of text. * Text Mode:: The major modes for editing text files. @@ -404,6 +405,43 @@ that separates pages (@pxref{Regexps}). The normal value of this variable is @code{"^\f"}, which matches a formfeed character at the beginning of a line. +@node Punctuation +@section Punctuation +@cindex Punctuation +@cindex Quotation marks +@cindex Electric Punct mode +@cindex mode, Electric Punct + One common way to quote is the typewriter convention, which quotes +uses apostrophes @samp{'like this'} or double-quotes @samp{"like +this"}. Another common way is the curved quote convention, which uses +left and right single and double quotation marks @samp{‘like this’} or +@samp{“like this”}. Typewriter quotes are simple and portable; curved +quotes are unambiguous and look nicer. + + Electric Punct mode makes it easier to type curved quotes. It +optionally converts a quotation using grave accent and apostrophe +@samp{`like this'}, to a form using single quotation marks @samp{‘like +this’}. Similarly, it converts a quotation using double grave accent +and double apostrophe @samp{``like this''} to a form using double +quotation marks @samp{“like this”}. + + You can customize the behavior of Electric Punct mode by setting +variables that control where it is active. If +@code{electric-punct-comment} is non-nil, it is active within +programming-language comments; if @code{electric-punct-string} is +non-nil, it is active within programming-language strings; and if +@code{electric-punct-paragraph} is non-nil, it is active within text +paragraphs. These variables default to nil, which causes Electric +Punct mode to be inactive everywhere. + + Electric Punct mode is enabled by default. To suppress it for a +single use, type @kbd{C-q `} or @kbd{C-q '} instead of @kbd{`} or +@kbd{'}. To toggle it, type @kbd{M-x electric-punct-mode}. To toggle +it in a single buffer, use @kbd{M-x electric-punct-local-mode}. To +insert a curved quote even when Electric Punct is disabled or +inactive, use @kbd{C-x 8 @key{RET}} (@code{insert-char}). +@xref{Inserting Text}. + @node Filling @section Filling Text @cindex filling text diff --git a/etc/NEWS b/etc/NEWS index 844181c..61a2c35 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -213,7 +213,8 @@ successive char insertions. ** Unicode names entered via C-x 8 RET now use substring completion by default. -** New minor mode global-eldoc-mode is enabled by default. +** New minor modes electric-punct-mode and global-eldoc-mode +are enabled by default. ** Emacs now supports "bracketed paste mode" when running on a terminal that supports it. This facility allows Emacs to understand pasted diff --git a/lisp/electric.el b/lisp/electric.el index dd7767f..bac1918 100644 --- a/lisp/electric.el +++ b/lisp/electric.el @@ -413,6 +413,98 @@ The variable `electric-layout-rules' says when and how to insert newlines." (remove-hook 'post-self-insert-hook #'electric-layout-post-self-insert-function)))) +;;; Electric punctuation. + +(defcustom electric-punct-comment nil + "Non-nil means to use electric punctuation in program comments." + :type 'boolean :safe 'booleanp :group 'electricity) + +(defcustom electric-punct-string nil + "Non-nil means to use electric punctuation in program strings." + :type 'boolean :safe 'booleanp :group 'electricity) + +(defcustom electric-punct-paragraph nil + "Non-nil means to use electric punctuation in text paragraphs." + :type 'boolean :safe 'booleanp :group 'electricity) + +(defun electric-punct-post-self-insert-function () + "Function that ‘electric-punct-mode’ adds to ‘post-self-insert-hook’. +This repunctuates when a punctuation key is typed." + (when (and electric-punct-mode enable-multibyte-characters + (memq last-command-event '(?' ?`))) + (let ((start + (if comment-start + (when (or electric-punct-comment electric-punct-string) + (let ((syntax (syntax-ppss))) + (and (or (and electric-punct-comment (nth 4 syntax)) + (and electric-punct-string (nth 3 syntax))) + (nth 8 syntax)))) + (and electric-punct-paragraph + (derived-mode-p 'text-mode) + (or (eq last-command-event ?`) + (save-excursion (backward-paragraph) (point))))))) + (when start + (save-excursion + (if (eq last-command-event ?`) + (cond ((re-search-backward "[`‘]`" (- (point) 2) t) + (replace-match "“")) + ((search-backward "`" (1- (point)) t) + (replace-match "‘"))) + (let ((pos (point))) + (if (memq (char-before (1- (point))) '(?' ?’)) + (when (and (search-backward "“" start t) + (eq pos (re-search-forward + "“\\([^”]*\\)['’]'" pos t))) + (replace-match "“\\1”")) + (when (and (search-backward "‘" start t) + (eq pos (re-search-forward + "‘\\([^’]*\\)'" pos t))) + (replace-match "‘\\1’")))))))))) + +(put 'electric-punct-post-self-insert-function 'priority 60) + +;;;###autoload +(define-minor-mode electric-punct-mode + "Toggle on-the-fly repunctuation (Electric Punctuation mode). +With a prefix argument ARG, enable Electric Punctuation mode if +ARG is positive, and disable it otherwise. If called from Lisp, +enable the mode if ARG is omitted or nil. + +When enabled, this replaces `foo bar' with ‘foo bar’ and replaces +``foo bar'' with “foo bar” as you type. This occurs only in +comments, strings, and text paragraphs, and these are selectively +controlled with ‘electric-punct-comment’, +‘electric-punct-string’, and ‘electric-punct-paragraph’. + +This is a global minor mode. To toggle the mode in a single buffer, +use ‘electric-punct-local-mode’." + :global t :group 'electricity + :initialize 'custom-initialize-delay + :init-value t + (if (not electric-punct-mode) + (unless (catch 'found + (dolist (buf (buffer-list)) + (with-current-buffer buf + (if electric-punct-mode (throw 'found t))))) + (remove-hook 'post-self-insert-hook + #'electric-punct-post-self-insert-function)) + (add-hook 'post-self-insert-hook + #'electric-punct-post-self-insert-function) + (electric--sort-post-self-insertion-hook))) + +;;;###autoload +(define-minor-mode electric-punct-local-mode + "Toggle ‘electric-punct-mode’ only in this buffer." + :variable (buffer-local-value 'electric-punct-mode (current-buffer)) + (cond + ((eq electric-punct-mode (default-value 'electric-punct-mode)) + (kill-local-variable 'electric-punct-mode)) + ((not (default-value 'electric-punct-mode)) + ;; Locally enabled, but globally disabled. + (electric-punct-mode 1) ; Setup the hooks. + (setq-default electric-punct-mode nil) ; But keep it globally disabled. + ))) + (provide 'electric) ;;; electric.el ends here -- 2.1.0