Last week I submitted a [post](https://www.reddit.com/r/emacs/comments/xqhbkt/newline_behavior_like_other_editors/) asking if Emacs had a native way of changing the behavior of the `newline` function so that it could work the way some other text editors do.
After interacting with some redditors and doing some more research I wrote [this comment](https://www.reddit.com/r/emacs/comments/xqhbkt/newline_behavior_like_other_editors/iqef2fk/) where I concluded that the way Sublime Text handles line composed of whitespace is the most intuitive for me, at least when editing Python source files. Which can be summarized as:
If I'm editing a buffer with the following Python code
if⋅True:$
⋅⋅⋅⋅if⋅True:$
⋅⋅⋅⋅⋅⋅⋅⋅print('2x⋅True')$
⋅⋅⋅⋅|
where `⋅` represents a space, `$` represents a newline and `|` represents the cursor, if I press Return I want Emacs to end up like
if⋅True:$
⋅⋅⋅⋅if⋅True:$
⋅⋅⋅⋅⋅⋅⋅⋅print('2x⋅True')$
$
⋅⋅⋅⋅|
instead of
if⋅True:$
⋅⋅⋅⋅if⋅True:$
⋅⋅⋅⋅⋅⋅⋅⋅print('2x⋅True')$
$
⋅⋅⋅⋅⋅⋅⋅⋅|
After reading the source code of `newline` and all the functions that are called by it I decided that redefining the function ` electric-indent-post-self-insert-function` would be the easiest way to achieve my goal. So I did it in the following way:
(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,
or if a member of `electric-indent-chars' was typed; but not in a string
or comment."
;; FIXME: This reindents the current line, but what we really want instead is
;; to reindent the whole affected text. That's the current line for simple
;; cases, but not all cases. We do take care of the newline case in an
;; ad-hoc fashion, but there are still missing cases such as the case of
;; electric-pair-mode wrapping a region with a pair of parens.
;; There might be a way to get it working by analyzing buffer-undo-list, but
;; it looks challenging.
(let (pos)
(when (and
electric-indent-mode
;; Don't reindent while inserting spaces at beginning of line.
(or (not (memq last-command-event '(?\s ?\t)))
(save-excursion (skip-chars-backward " \t") (not (bolp))))
(setq pos (electric--after-char-pos))
(save-excursion
(goto-char pos)
(let ((act (or (run-hook-with-args-until-success
'electric-indent-functions
last-command-event)
(memq last-command-event electric-indent-chars))))
(not (memq act '(nil no-indent))))))
;; If we error during indent, silently give up since this is an
;; automatic action that the user didn't explicitly request.
;; But we don't want to suppress errors from elsewhere in *this*
;; function, hence the `condition-case' and `throw' (Bug#18764).
(catch 'indent-error
;; For newline, we want to reindent both lines and basically
;; behave like reindent-then-newline-and-indent (whose code we
;; hence copied).
(let ( (at-newline (<= pos (line-beginning-position)))
(whitespace-indentation nil) ;; new variable introduced
)
(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)
;; Don't reindent the previous line if the
;; indentation function is not a real one.
(goto-char before)
(condition-case-unless-debug ()
(indent-according-to-mode)
(error (throw 'indent-error nil)))
)
(unless (eq electric-indent-inhibit 'electric-layout-mode)
;; Unless we're operating under
;; `electric-layout-mode' (Bug#35254), the goal here
;; will be to remove the trailing whitespace after
;; reindentation of the previous line because that
;; may have (re)introduced it.
(goto-char before)
(when (looking-back "^[[:space:]]*$") (setq whitespace-indentation (current-indentation))) ;;keep the indentation of an whitespace line before it is deleted
;; We were at EOL in marker `before' before the call
;; to `indent-according-to-mode' but after we may
;; not be (Bug#15767).
(when (and (eolp))
(delete-horizontal-space t)
)
))))
(unless (and electric-indent-inhibit
(not at-newline))
(condition-case-unless-debug ()
(if whitespace-indentation
(indent-to whitespace-indentation)
(indent-according-to-mode))
(error (throw 'indent-error nil)))))))))
All I did was add this 2 lines
(whitespace-indentation nil)
(when (looking-back "^[[:space:]]*$") (setq whitespace-indentation (current-indentation)))
and this conditional
(if whitespace-indentation (indent-to whitespace-indentation) (indent-according-to-mode))
As this is my first time writing Elisp and messing with the source code of Emacs, I'm not really certain of what I'm doing. So I want to ask for suggestions on how to improve these changes, in order to avoid unwanted behaviors and bugs.
Thanks