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?

2
  • I don't think there are any convenient default keybindings, but you could easily create some yourself.
    – shosti
    Sep 23, 2014 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 <return> to newline-and-indent Sep 23, 2014 at 22:59

4 Answers 4

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.

3
  • 1
    (add-hook 'post-self-insert-hook 'indent-pp-sexp) works surprising well. Sep 23, 2014 at 23:05
  • Looks good. That might be costly in some modes, but it's a nice approach nevertheless.
    – Dmitry
    Sep 23, 2014 at 23:28
  • For Smartparens users, you can reindent the current defun with M-x sp-indent-defun. I bind this to C-M-q. Jul 13, 2018 at 23:18
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)))
9
  • Would be excellent if this silenced auto-indent messages and better handled languages that end lines with semicolons. Sep 28, 2014 at 13:10
  • @holocronweaver sure, I can try that. Which major mode has indent messages? And what's the issue with semi colons?
    – Malabarba
    Sep 28, 2014 at 14:44
  • All the modes I tried on Emacs trunk produced indent messages, including elisp. Since semicolons end a line in many languages, until you type the semicolon the following line jumps about as though it were part of the current line. Sep 28, 2014 at 20:04
  • @holocronweaver Silenced the messages. Thanks for pointing it out.
    – Malabarba
    Sep 29, 2014 at 8:16
  • Thanks for the fix! One solution to the semicolon problem is to only indent in c-mode languages when a semicolon is typed. Sep 29, 2014 at 15:00
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.

1

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.