After a good bit of digging and learning some features of Emacs Lisp, I managed to make a hack which produces the effect that I desire. I imagine that I'm not the only one who wants to leave trailing whitespace (at least in some scenarios), so here it is:
;; Hack to leave trailing whitespace (eval-after-load 'electric '(fset 'electric-indent-post-self-insert-function (lambda () "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 (or (memq act '(nil no-indent)) ;; In a string or comment. (unless (eq act 'do-indent) (nth 8 (syntax-ppss)))))))) ;; 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)))) (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) (indent-according-to-mode)) ;; We are at EOL before the call to indent-according-to-mode, and ;; after it we usually are as well, but not always. We tried to ;; address it with `save-excursion' but that uses a normal marker ;; whereas we need `move after insertion', so we do the ;; save/restore by hand. (goto-char before)))) (unless (and electric-indent-inhibit (not at-newline)) (indent-according-to-mode))))))))
This is just a slightly different version of the electric-indent-post-self-insert-function which is defined in electric.el. The only difference is that I removed the following code from the hacked version of the function:
... (when (eolp) ;; Remove the trailing whitespace after indentation because ;; indentation may (re)introduce the whitespace. (delete-horizontal-space t))...
Here is my hack in action (I'm not running it in emacs -Q, since that seems to strip away my hack).
emacs -Q. State, for each step, what you expected to happen and what you saw instead.