From 7100ff98443a6d433bbce9de8771983eb13f03c0 Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Tue, 16 Nov 2010 16:14:46 -0500 Subject: * lisp/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. --- lisp/electric.el | 127 +++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 96 insertions(+), 31 deletions(-) (limited to 'lisp/electric.el') 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 -- cgit v1.2.3-70-g09d2