diff --git a/lisp/electric.el b/lisp/electric.el index 302fb8d08bc..7825bbe43ec 100644 --- a/lisp/electric.el +++ b/lisp/electric.el @@ -192,6 +192,29 @@ Returns nil when we can't find this char." ;;; Electric indentation. +(defcustom electric-indent-actions nil + "List of actions to indent. + +The valid elements of this list can be: + - yank: Indent the yanked text only if point is not in a string or + comment and yanked region is longer than 1 line. + - save: Indent the whole buffer before saving it. + +The indentation will not happen when the major mode is unable to +reindent code reliably, such as in buffers where indentation is +significant." + :type '(repeat (choice (const :tag "After yanking" yank) + (const :tag "Before saving" before-save))) + :set (lambda (var val) + (set-default var val) + (when electric-indent-mode + (electric-indent-mode -1) + (electric-indent-mode +1))) + :safe (lambda (v) + (and (proper-list-p v) + (null (seq-filter (lambda (e) (not (symbolp e)) ) v)))) + :version "31.1") + ;; Autoloading variables is generally undesirable, but major modes ;; should usually set this variable by adding elements to the default ;; value, which only works well if the variable is preloaded. @@ -218,6 +241,50 @@ If `indent-line-function' is one of those, then `electric-indent-mode' will not try to reindent lines. It is normally better to make the major mode set `electric-indent-inhibit', but this can be used as a workaround.") +(defun electric-indent-can-reindent-p () + "Return t if `electric-indent-mode' can performs reindentation." + (not (or (memq indent-line-function + electric-indent-functions-without-reindent) + electric-indent-inhibit))) + +(defun electric-indent--yank-advice (fn &rest r) + (let ((p (point)) + (end (line-beginning-position))) + (apply fn r) + (when (and electric-indent-mode + (memq 'yank electric-indent-actions) + (electric-indent-can-reindent-p) + ;; Ensure yanked text is longer than 1 line + (> (point) p) + (not (= end (line-beginning-position)))) + (undo-boundary) + (save-excursion + (with-demoted-errors "Error reindenting: %S" + (indent-region p (point))))))) + +(defun electric-indent-save-hook () + (when (and electric-indent-mode + ;; Ensure this hook is called interactively + (memq real-this-command '(save-buffer basic-save-buffer)) + (memq 'before-save electric-indent-actions) + (not buffer-read-only) + (electric-indent-can-reindent-p)) + (save-excursion + (with-demoted-errors "Error reindenting: %S" + (indent-region (point-min) (point-max)))))) + +(defun electric-indent-toggle-indent-actions (enable) + "Enable the actions specified in `electric-indent-actions'." + (cond + ((memq 'yank electric-indent-actions) + (if enable + (advice-add #'yank :around #'electric-indent--yank-advice) + (advice-remove #'yank #'electric-indent--yank-advice))) + ((memq 'before-save electric-indent-actions) + (if enable + (add-hook 'before-save-hook #'electric-indent-save-hook) + (remove-hook 'before-save-hook #'electric-indent-save-hook))))) + (defun electric-indent-post-self-insert-function () "Function that `electric-indent-mode' adds to `post-self-insert-hook'. This indents if the hook `electric-indent-functions' returns non-nil, @@ -256,10 +323,7 @@ or comment." (when at-newline (let ((before (copy-marker (1- pos) t))) (save-excursion - (unless - (or (memq indent-line-function - electric-indent-functions-without-reindent) - electric-indent-inhibit) + (when (electric-indent-can-reindent-p) ;; Don't reindent the previous line if the ;; indentation function is not a real one. (goto-char before) @@ -331,9 +395,13 @@ use `electric-indent-local-mode'." (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 - 60))) + 60)) + + ;; Toggle the reindentation on actions + (electric-indent-toggle-indent-actions electric-indent-mode)) ;;;###autoload (define-minor-mode electric-indent-local-mode