18

Say I have a elisp code like:

(+ 2 3▮(+ 3
          4))

Is there a way to automatically re-indent the sexp after the cursor, when I add or remove symbols?

So after pressing SPC 4 SPC, I would automatically get:

(+ 2 3 4 ▮(+ 3
             4))

I can do this manual by calling mark-sexp followed by indent-region. Are there better ways of doing this?

Maciej Goszczycki
  • 1,777
  • 13
  • 18
  • I don't think there are any convenient default keybindings, but you could easily create some yourself. – shosti Sep 23 '14 at 22:56
  • I know but I'm specifically wondering are there any _better_ ways of doing this. For example how `electric-indent-mode` is much better than mapping `` to `newline-and-indent` – Maciej Goszczycki Sep 23 '14 at 22:59

4 Answers4

16

Aggressive Indent Mode

Since some people asked for it, I turned this answer into a package.

If you have Melpa configured, you can install it with

M-x package-install RET aggressive-indent

See the Readme for all the options, but the simplest way to turn it on is:

(add-hook 'emacs-lisp-mode-hook #'aggressive-indent-mode)

The Old Answer

The following does auto-indentation only on elisp buffers. It has advantage of also working when you erase or yank stuff (instead of just typing). It's easy to add to other modes as well.

This function will indent whatever s-expression the point is currently inside. You can bind it to a key if you'd like, but see below first.

(require 'cl-lib)

(defun endless/indent-defun ()
  "Indent current defun.
Do nothing if mark is active (to avoid deactivating it), or if
buffer is not modified (to avoid creating accidental
modifications)."
  (interactive)
  (ignore-errors
    (unless (or (region-active-p)
                buffer-read-only
                (null (buffer-modified-p)))
      (let ((l (save-excursion (beginning-of-defun 1) (point)))
            (r (save-excursion (end-of-defun 1) (point))))
        (cl-letf (((symbol-function 'message) #'ignore))
          (indent-region l r))))))

This hook will make it so that this function will be run after you type anything, but only in elisp buffers. This should keep everything always indented.

(add-hook
 'emacs-lisp-mode-hook
 (lambda ()
   (add-hook 'post-command-hook
             #'endless/indent-defun nil 'local)))

Try it! It works remarkably well.

Also, following @holocronweaver’s suggestion in the comments, you can use something like the following for c-like languages:

(define-key c++-mode-map ";"
  (lambda () (interactive)
    (insert ";")
    (endless/indent-defun)))
Malabarba
  • 22,878
  • 6
  • 78
  • 163
14

Instead of mark-sexp + indent-region, you can press C-M-q. This will call indent-pp-sexp. Not automatic, but a bit better than having to invoke two commands.

Or if you're using paredit-mode, press M-q. That will reindent the whole function definition you're in.

Sigma
  • 4,510
  • 21
  • 27
Dmitry
  • 3,508
  • 15
  • 20
3

I don't know of a pre-existing solution, but you can use post-self-insert-hook to accomplish this yourself:

(defun my-indent-next-sexp ()
  (interactive)
  (mark-sexp)
  (indent-region))

(add-hook 'post-self-insert-hook 'my-indent-next-sexp)

I would worry about potential performance problems though.

shosti
  • 5,048
  • 26
  • 33
1

You can try el-fly-indent-mode, which is more well-behaved than aggressive-indent-mode when dealing with Elisp.

Drew
  • 75,699
  • 9
  • 109
  • 225
Jiahao
  • 11
  • 1