4

I'm using Emacs 25.1.50 (9.0) on OS X and am starting to get annoyed with the automatic removal of trailing whitespace. I have tried solutions from here, but to no avail as my write-file-functions and before-save-hook variables are nil to begin with. I've also tried setting delete-trailing-lines to nil, but that still hasn't fixed my issue. I also found this exact issue that I'm having for Sublime Text, but also found a fix for it. I imagine that if the issue was able to be fixed so easily there, it should be simple to do in Emacs.

EDIT:

As per @Drew's request, I've decided to add some more info since I admittedly initially wrote this in a hurry. Upon starting Emacs 25 with the -Q flag, I notice the same issue being demonstrated as in my usual, customized Emacs. Here is a demo of the issue in emacs -Q. As you can see, in certain major modes that do auto-indentation, when I press RET on a line that I haven't written anything on, the line loses its whitespace formatting as my point goes on to the next line. This is irritating when I want to leave that whitespace formatting in between lines.

2
  • 3
    The question is unclear. Provide a step-by-step recipe to repro the problem, starting from emacs -Q. State, for each step, what you expected to happen and what you saw instead.
    – Drew
    Commented Apr 26, 2016 at 14:29
  • @Drew I added more info in the edit above.
    – GDP2
    Commented Apr 26, 2016 at 17:49

3 Answers 3

7

This is a feature of electric-indent-mode (which is on by default in recent versions of Emacs). The behaviour is hard-coded in electric-indent-post-self-insert-function.

I think that very few people would actually want what you're asking for (i.e. that trailing whitespace is introduced automatically, and then left there). It's rather more common for people to want to get rid of trailing whitespace, not to find new ways to create it!

So disable electric-indent-mode. You'll need to indent manually, or find some alternative approach to automated indentation which leaves whitespace-only lines alone.

4
  • Darn, that sucks. I think I'll look into hacking electric-indent-mode to do my bidding in this regard since that's the case. If that fails, then I might try out an alternative solution. Thanks for the advice, though; I'll post a fix if I find one.
    – GDP2
    Commented Apr 26, 2016 at 22:43
  • @GDP2 Maybe send a patch to add an option, or make a feature request?
    – xuhdev
    Commented Apr 27, 2016 at 10:40
  • @xuhdev Not a bad idea; I'll definitely consider that since I haven't dug too deeply into this yet and am not sure how complicated it may be.
    – GDP2
    Commented Apr 27, 2016 at 16:49
  • @xuhdev Fyi, I added a fix for this; see my answer.
    – GDP2
    Commented Apr 28, 2016 at 2:58
6

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).

0

Here is how I turn on deletion of trailing whitespace on Mac OSX, so maybe you could reverse what I do to leave your whitespace on. Notice that I set the variable "delete-trailing-WHITESPACE" (not -trailing-lines).

(setq-default show-trailing-whitespace t)
(add-hook 'before-save-hook 'delete-trailing-whitespace)
4
  • What on earth is the "reverse" of removing trailing whitespace when the file is saved?
    – phils
    Commented Apr 26, 2016 at 21:09
  • Ah, I see now that this answer was written before the question was clarified. I tried to reverse my down-vote, but too much time has elapsed (unless the answer is edited).
    – phils
    Commented Apr 26, 2016 at 21:30
  • Hi, thanks for the suggestion. First off, I'm pretty sure that show-trailing-whitespace only does exactly what it says on the tin, which is emphasize trailing whitespace. Secondly, as I clarified in my edit, I don't think that this issue is related to saving a given file, and it may have to do with electric-indent-mode according to @phils. One more nitpick is that delete-trailing-whitespace is a function, not a variable :P Again, thanks for the suggestion, though.
    – GDP2
    Commented Apr 26, 2016 at 22:36
  • @Kevin Fyi, I added a fix for this; see my answer.
    – GDP2
    Commented Apr 28, 2016 at 2:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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