C-d to close eshell

Published on:

One of the "tricks" that I have learned to use when working with terminals is using C-d to close them, or when working on a TTY logout. It somehow grew to the extent that if I can't use it, I get annoyed, like with eshell.

I have customized ansi-term to immediately close its buffer after the shell quits. This makes it very easy to start an ansi-term, which I've bound to C-c t, run a quick command (perhaps make, or similar), C-d, and I'm out. I want that for my eshell too.

There are a few conditions that I want met before the buffer is killed, though.

  1. Since eshell is an Emacs mode like any other, C-d is usually used to forward-kill characters, I don't want to lose this.
  2. I only want it to quit when the line of input is empty.

The following piece of code make sure these conditions are met.

  1. It interactively calls delete-char, which keeps keybindings like C-4 C-d to delete 4 characters working.
  2. It catches the error condition which is signaled whenever delete-char can't do it's job (like when there's nothing left to delete in the buffer).
  3. It checks to make sure that the signaled error is the end-of-buffer error. I don't want to kill the buffer if I try to delete more characters than are in the buffer because I feel that could cause irritating surprises.
  4. It checks of the cursor is at the eshell prompt. This, combined with only responding to the end-of-buffer error, makes sure we're on an empty line and not just at the end of the input. Sometimes keys are pressed at the wrong time and I don't want to have to re-type a command just because I was being an idiot.
  5. If the right conditions aren't met, signal the error again so I can see what's going on.
(defun eshell-C-d ()
  "Either call `delete-char' interactively or quit."
  (interactive)
  (condition-case err
      (call-interactively #'delete-char)
    (error (if (and (eq (car err) 'end-of-buffer)
                    (looking-back eshell-prompt-regexp))
               (kill-buffer)
             (signal (car err) (cdr err))))))

I then bind this to C-d in eshell.

(add-hook 'eshell-mode-hook
          (lambda () (local-set-key (kbd "C-d") #'eshell-C-d)))