diff options
-rw-r--r-- | etc/NEWS | 3 | ||||
-rw-r--r-- | lisp/ChangeLog | 9 | ||||
-rw-r--r-- | lisp/electric.el | 127 |
3 files changed, 107 insertions, 32 deletions
@@ -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 |