Commit 3b843809 authored by Stefan Monnier's avatar Stefan Monnier

* lisp/electric.el (electricity): New group.

(electric-indent-chars): New var.
(electric-indent-post-self-insert-function): New fun.
(electric-indent-mode): New minor mode.
(electric-pair-skip-self): New custom.
(electric-pair-post-self-insert-function): New function.
(electric-pair-mode): New minor mode.
parent 0da20854
......@@ -427,6 +427,8 @@ system or session bus.
* New Modes and Packages in Emacs 24.1
** New global minor modes electric-pair-mode and electric-indent-mode.
** pcase.el provides the ML-style pattern matching macro `pcase'.
** smie.el is a package providing a simple generic indentation engine.
2010-09-03 Stefan Monnier <>
* electric.el (electricity): New group.
(electric-indent-chars): New var.
(electric-indent-post-self-insert-function): New fun.
(electric-indent-mode): New minor mode.
(electric-pair-skip-self): New custom.
(electric-pair-post-self-insert-function): New function.
(electric-pair-mode): New minor mode.
* calc/calc-aent.el (calcAlg-blink-matching-check): New fun, to replace
(calc-alg-ent-map, calc-alg-ent-esc-map): Initialize in the declaration.
......@@ -24,10 +24,23 @@
;;; Commentary:
; zaaaaaaap
;; "Electric" has been used in Emacs to refer to different things.
;; Among them:
;; - electric modes and buffers: modes that typically pop-up in a modal kind of
;; way a transient buffer that automatically disappears as soon as the user
;; is done with it.
;; - electric keys: self inserting keys which additionally perform some side
;; operation which happens to be often convenient at that time. Examples of
;; such side operations are: reindenting code, inserting a newline,
;; ... auto-fill-mode and abbrev-mode can be considered as built-in forms of
;; electric key behavior.
;;; Code:
(eval-when-compile (require 'cl))
;; This loop is the guts for non-standard modes which retain control
;; until some event occurs. It is a `do-forever', the only way out is
;; to throw. It assumes that you have set up the keymap, window, and
......@@ -157,6 +170,131 @@
(fit-window-to-buffer win max-height))
;;; Electric keys.
(defgroup electricity ()
"Electric behavior for self inserting keys."
:group 'editing)
;; Electric indentation.
(defvar electric-indent-chars '(?\n)
"Characters that should cause automatic reindentation.")
(defun electric-indent-post-self-insert-function ()
;; FIXME: This reindents the current line, but what we really want instead is
;; to reindent the whole affected text. That's the current line for simple
;; cases, but not all cases. We do take care of the newline case in an
;; ad-hoc fashion, but there are still missing cases such as the case of
;; electric-pair-mode wrapping a region with a pair of parens.
;; There might be a way to get it working by analyzing buffer-undo-list, but
;; it looks challenging.
(when (and (memq last-command-event electric-indent-chars)
;; Don't reindent while inserting spaces at beginning of line.
(or (not (memq last-command-event '(?\s ?\t)))
(save-excursion (skip-chars-backward " \t") (not (bolp))))
;; Not in a string or comment.
(not (nth 8 (syntax-ppss))))
;; For newline, we want to reindent both lines and basically behave like
;; reindent-then-newline-and-indent (whose code we hence copied).
(when (and (eq last-command-event ?\n)
;; Sanity check.
(eq (char-before) last-command-event))
(let ((pos (copy-marker (1- (point)) t)))
(goto-char pos)
;; We are at EOL before the call to indent-according-to-mode, and
;; after it we usually are as well, but not always. We tried to
;; address it with `save-excursion' but that uses a normal marker
;; whereas we need `move after insertion', so we do the
;; save/restore by hand.
(goto-char pos)
;; Remove the trailing whitespace after indentation because
;; indentation may (re)introduce the whitespace.
(delete-horizontal-space t))))
(define-minor-mode electric-indent-mode
"Automatically reindent lines of code when inserting particular chars.
`electric-indent-chars' specifies the set of chars that should cause reindentation."
:global t
:group 'electricity
(if electric-indent-mode
(add-hook 'post-self-insert-hook
(remove-hook 'post-self-insert-hook
;; Electric pairing.
(defcustom electric-pair-skip-self t
"If non-nil, skip char instead of inserting a second closing paren.
When inserting a closing paren character right before the same character,
just skip that character instead, so that hitting ( followed by ) results
in \"()\" rather than \"())\".
This can be convenient for people who find it easier to hit ) than C-f."
:type 'boolean)
(defun electric-pair-post-self-insert-function ()
(let* ((syntax (and (eq (char-before) last-command-event) ; Sanity check.
(char-syntax last-command-event)))
;; FIXME: when inserting the closer, we should maybe use
;; self-insert-command, although it may prove tricky running
;; post-self-insert-hook recursively, and we wouldn't want to trigger
;; blink-matching-open.
(closer (if (eq syntax ?\()
(cdr (aref (syntax-table) last-command-event))
;; Wrap a pair around the active region.
((and (memq syntax '(?\( ?\" ?\$)) (use-region-p))
(if (> (mark) (point))
(goto-char (mark))
;; We already inserted the open-paren but at the end of the region,
;; so we have to remove it and start over.
(delete-char -1)
(goto-char (mark))
(insert last-command-event)))
(insert closer))
;; Backslash-escaped: no pairing, no skipping.
(goto-char (1- (point)))
(not (zerop (% (skip-syntax-backward "\\") 2))))
;; Skip self.
((and (memq syntax '(?\) ?\" ?\$))
(eq (char-after) last-command-event))
;; This is too late: rather than insert&delete we'd want to only skip (or
;; insert in overwrite mode). The difference is in what goes in the
;; undo-log and in the intermediate state which might be visible to other
;; post-self-insert-hook. We'll just have to live with it for now.
(delete-char 1))
;; Insert matching pair.
((not (or (not (memq syntax `(?\( ?\" ?\$)))
;; I find it more often preferable not to pair when the
;; same char is next.
(eq last-command-event (char-after))
(eq last-command-event (char-before (1- (point))))
;; I also find it often preferable not to pair next to a word.
(eq (char-syntax (following-char)) ?w)))
(save-excursion (insert closer))))))
(define-minor-mode electric-pair-mode
"Automatically pair-up parens when inserting an open paren."
:global t
:group 'electricity
(if electric-pair-mode
(add-hook 'post-self-insert-hook
(remove-hook 'post-self-insert-hook
(provide 'electric)
;; arch-tag: dae045eb-dc2d-4fb7-9f27-9cc2ce277be8
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment