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?

  • 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 <return> to newline-and-indent – Maciej Goszczycki Sep 23 '14 at 22:59
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.

  • 1
    (add-hook 'post-self-insert-hook 'indent-pp-sexp) works surprising well. – Maciej Goszczycki Sep 23 '14 at 23:05
  • Looks good. That might be costly in some modes, but it's a nice approach nevertheless. – Dmitry Sep 23 '14 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. – Radon Rosborough Jul 13 '18 at 23:18
15

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)))
  • Would be excellent if this silenced auto-indent messages and better handled languages that end lines with semicolons. – holocronweaver Sep 28 '14 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 '14 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. – holocronweaver Sep 28 '14 at 20:04
  • @holocronweaver Silenced the messages. Thanks for pointing it out. – Malabarba Sep 29 '14 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. – holocronweaver Sep 29 '14 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, privacy policy and cookie policy

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