+(define-minor-mode electric-indent-mode
+ "Toggle on-the-fly reindentation (Electric Indent mode).
+With a prefix argument ARG, enable Electric Indent mode if ARG is
+positive, and disable it otherwise. If called from Lisp, enable
+the mode if ARG is omitted or nil.
+
+When enabled, this reindents whenever the hook `electric-indent-functions'
+returns non-nil, or if you insert a character from `electric-indent-chars'.
+
+This is a global minor mode. To toggle the mode in a single buffer,
+use `electric-indent-local-mode'."
+ :global t :group 'electricity
+ :initialize 'custom-initialize-delay
+ :init-value t
+ (if (not electric-indent-mode)
+ (unless (catch 'found
+ (dolist (buf (buffer-list))
+ (with-current-buffer buf
+ (if electric-indent-mode (throw 'found t)))))
+ (remove-hook 'post-self-insert-hook
+ #'electric-indent-post-self-insert-function))
+ (add-hook 'post-self-insert-hook
+ #'electric-indent-post-self-insert-function)
+ (electric--sort-post-self-insertion-hook)))
+
+;;;###autoload
+(define-minor-mode electric-indent-local-mode
+ "Toggle `electric-indent-mode' only in this buffer."
+ :variable (buffer-local-value 'electric-indent-mode (current-buffer))
+ (cond
+ ((eq electric-indent-mode (default-value 'electric-indent-mode))
+ (kill-local-variable 'electric-indent-mode))
+ ((not (default-value 'electric-indent-mode))
+ ;; Locally enabled, but globally disabled.
+ (electric-indent-mode 1) ; Setup the hooks.
+ (setq-default electric-indent-mode nil) ; But keep it globally disabled.
+ )))
+
+;;; Electric newlines after/before/around some chars.
+
+(defvar electric-layout-rules nil
+ "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', `after-stay', or a
+function of no arguments that returns one of those symbols.
+
+The symbols specify where in relation to CHAR the newline
+character(s) should be inserted. `after-stay' means insert a
+newline after CHAR but stay in the same place.")
+
+(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)))
+ (sym (if (functionp rule) (funcall rule) rule)))
+ (set-marker-insertion-type end (not (eq sym 'after-stay)))
+ (goto-char pos)
+ (pcase sym
+ ;; 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.
+ ;;
+ ;; FIXME: check eolp before inserting \n?
+ (`before (goto-char (1- pos)) (skip-chars-backward " \t")
+ (unless (bolp) (insert "\n")))
+ (`after (insert "\n"))
+ (`after-stay (save-excursion
+ (let ((electric-layout-rules nil))
+ (newline 1 t))))
+ (`around (save-excursion
+ (goto-char (1- pos)) (skip-chars-backward " \t")
+ (unless (bolp) (insert "\n")))
+ (insert "\n"))) ; FIXME: check eolp before inserting \n?
+ (goto-char end)))))
+
+(put 'electric-layout-post-self-insert-function 'priority 40)
+
+;;;###autoload
+(define-minor-mode electric-layout-mode
+ "Automatically insert newlines around some chars.
+With a prefix argument ARG, enable Electric Layout mode if ARG is
+positive, and disable it otherwise. If called from Lisp, enable
+the mode if ARG is omitted or nil.
+The variable `electric-layout-rules' says when and how to insert newlines."
+ :global t :group 'electricity
+ (cond (electric-layout-mode
+ (add-hook 'post-self-insert-hook
+ #'electric-layout-post-self-insert-function)
+ (electric--sort-post-self-insertion-hook))
+ (t
+ (remove-hook 'post-self-insert-hook
+ #'electric-layout-post-self-insert-function))))
+