summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--etc/NEWS3
-rw-r--r--lisp/ChangeLog9
-rw-r--r--lisp/electric.el127
3 files changed, 107 insertions, 32 deletions
diff --git a/etc/NEWS b/etc/NEWS
index 5c24e1d5fd9..7fb80151f36 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -543,7 +543,8 @@ system or session bus.
* New Modes and Packages in Emacs 24.1
-** New global minor modes electric-pair-mode and electric-indent-mode.
+** New global minor modes electric-pair-mode, electric-indent-mode,
+and electric-layout-mode.
** pcase.el provides the ML-style pattern matching macro `pcase'.
diff --git a/lisp/ChangeLog b/lisp/ChangeLog
index bf31d51c2d1..b5be8544e5a 100644
--- a/lisp/ChangeLog
+++ b/lisp/ChangeLog
@@ -1,3 +1,12 @@
+2010-11-16 Stefan Monnier <monnier@iro.umontreal.ca>
+
+ * electric.el (electric-layout-mode): New minor mode.
+ (electric--after-char-pos): New function.
+ (electric-indent-post-self-insert-function): Use it.
+ (electric-layout-rules): New var.
+ (electric-layout-post-self-insert-function): New function.
+ (electric-indent-mode): Make them interact better.
+
2010-11-15 Stefan Monnier <monnier@iro.umontreal.ca>
* emacs-lisp/checkdoc.el (checkdoc-syntax-table): Fix last change.
diff --git a/lisp/electric.el b/lisp/electric.el
index a0d849bbcca..b34d327c4f2 100644
--- a/lisp/electric.el
+++ b/lisp/electric.el
@@ -176,6 +176,20 @@
"Electric behavior for self inserting keys."
:group 'editing)
+(defun electric--after-char-pos ()
+ "Return the position after the char we just inserted.
+Returns nil when we can't find this char."
+ (let ((pos (point)))
+ (when (or (eq (char-before) last-command-event) ;; Sanity check.
+ (save-excursion
+ (or (progn (skip-chars-backward " \t")
+ (setq pos (point))
+ (eq (char-before) last-command-event))
+ (progn (skip-chars-backward " \n\t")
+ (setq pos (point))
+ (eq (char-before) last-command-event)))))
+ pos)))
+
;; Electric indentation.
;; Autoloading variables is generally undesirable, but major modes
@@ -193,35 +207,35 @@
;; 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)
- ;; Don't reindent the previous line if the indentation function
- ;; is not a real one.
- (not (memq indent-line-function
- '(indent-relative indent-relative-maybe)))
- ;; Sanity check.
- (eq (char-before) last-command-event))
- (let ((pos (copy-marker (1- (point)) t)))
- (save-excursion
- (goto-char pos)
- (indent-according-to-mode)
- ;; 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))))
- (indent-according-to-mode)))
+ (let (pos)
+ (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))))
+ (setq pos (electric--after-char-pos))
+ ;; Not in a string or comment.
+ (not (nth 8 (save-excursion (syntax-ppss pos)))))
+ ;; For newline, we want to reindent both lines and basically behave like
+ ;; reindent-then-newline-and-indent (whose code we hence copied).
+ (when (and (< (1- pos) (line-beginning-position))
+ ;; Don't reindent the previous line if the indentation
+ ;; function is not a real one.
+ (not (memq indent-line-function
+ '(indent-relative indent-relative-maybe))))
+ (let ((before (copy-marker (1- pos) t)))
+ (save-excursion
+ (goto-char before)
+ (indent-according-to-mode)
+ ;; 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 before)
+ ;; Remove the trailing whitespace after indentation because
+ ;; indentation may (re)introduce the whitespace.
+ (delete-horizontal-space t))))
+ (indent-according-to-mode))))
;;;###autoload
(define-minor-mode electric-indent-mode
@@ -233,7 +247,17 @@
(add-hook 'post-self-insert-hook
#'electric-indent-post-self-insert-function)
(remove-hook 'post-self-insert-hook
- #'electric-indent-post-self-insert-function)))
+ #'electric-indent-post-self-insert-function))
+ ;; FIXME: electric-indent-mode and electric-layout-mode interact
+ ;; in non-trivial ways. It turns out that electric-indent-mode works
+ ;; better if it is run *after* electric-layout-mode's hook.
+ (when (memq #'electric-layout-post-self-insert-function
+ (memq #'electric-indent-post-self-insert-function
+ (default-value 'post-self-insert-hook)))
+ (remove-hook 'post-self-insert-hook
+ #'electric-layout-post-self-insert-function)
+ (add-hook 'post-self-insert-hook
+ #'electric-layout-post-self-insert-function)))
;; Electric pairing.
@@ -302,7 +326,48 @@ This can be convenient for people who find it easier to hit ) than C-f."
#'electric-pair-post-self-insert-function)
(remove-hook 'post-self-insert-hook
#'electric-pair-post-self-insert-function)))
-
+
+;; Automatically add newlines after/before/around some chars.
+
+(defvar electric-layout-rules '()
+ "List of rules saying where to automatically insert newlines.
+Each rule has the form (CHAR . WHERE) where CHAR is the char
+that was just inserted and WHERE specifies where to insert newlines
+and can be: nil, `before', `after', `around', or a function that returns
+one of those symbols.")
+
+(defun electric-layout-post-self-insert-function ()
+ (let* ((rule (cdr (assq last-command-event electric-layout-rules)))
+ pos)
+ (when (and rule
+ (setq pos (electric--after-char-pos))
+ ;; Not in a string or comment.
+ (not (nth 8 (save-excursion (syntax-ppss pos)))))
+ (let ((end (copy-marker (point) t)))
+ (goto-char pos)
+ (case (if (functionp rule) (funcall rule) rule)
+ ;; FIXME: we used `newline' down here which called
+ ;; self-insert-command and ran post-self-insert-hook recursively.
+ ;; It happened to make electric-indent-mode work automatically with
+ ;; electric-layout-mode (at the cost of re-indenting lines
+ ;; multiple times), but I'm not sure it's what we want.
+ (before (goto-char (1- pos)) (insert "\n"))
+ (after (insert "\n"))
+ (around (goto-char (1- pos)) (insert "\n")
+ (forward-char 1) (insert "\n")))
+ (goto-char end)))))
+
+;;;###autoload
+(define-minor-mode electric-layout-mode
+ "Automatically insert newlines around some chars."
+ :global t
+ :group 'electricity
+ (if electric-layout-mode
+ (add-hook 'post-self-insert-hook
+ #'electric-layout-post-self-insert-function)
+ (remove-hook 'post-self-insert-hook
+ #'electric-layout-post-self-insert-function)))
+
(provide 'electric)
;; arch-tag: dae045eb-dc2d-4fb7-9f27-9cc2ce277be8